Aşağıdaki size kodu verip daha sonra ise adım adım açıklayacağım. Ekran çıktıları vererek ise çalışmayı görselleştireceğim.
import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/svg.dart';
import 'package:harpia_project/utils/Utils.dart';
import 'package:harpia_project/views/loginscreen.dart';
import 'package:harpia_project/views/network/network_error.dart';
import 'package:harpia_project/utils/MySharedPreferences.dart';
import 'package:harpia_project/views/preview.dart';
import 'package:get_storage/get_storage.dart';
ThemeMode appTheme = ThemeMode.dark; // Varsayılan tema
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
await EasyLocalization.ensureInitialized();
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(statusBarColor: Colors.transparent));
runApp(
EasyLocalization(
supportedLocales: const [
Locale('en'),
Locale('tr'),
Locale('fr'),
Locale('zh'),
],
path: 'assets/translations',
fallbackLocale: const Locale('en'),
child: MyApp()),
);
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(300, 800),
builder: (context, child) => MaterialApp(
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
debugShowCheckedModeBanner: false,
home: SplashScreen(),
),
);
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
@override
SplashScreenState createState() => SplashScreenState();
}
class SplashScreenState extends State<SplashScreen> {
Color selectedBackgroundColor = Colors.white; // Seçilen arka plan rengi
@override
void initState() {
super.initState();
_checkInternetAndNavigate();
loadBackgroundColor();
}
@override
Widget build(BuildContext context) {
MySharedPreferences.setLocalName(Platform.localeName);
// Enable immersive mode
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
return Scaffold(
backgroundColor: selectedBackgroundColor,
body: Stack(
children: [
// First LinearLayout with background image
Container(
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/header_login.png'),
fit: BoxFit.fill,
),
),
),
// Centered Logo ImageView
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
'assets/images/dikeycizgili.svg',
width: 100,
height: 100,
),
// TextView with "step_by_step" text
const SizedBox(
height: 25,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 3.sp),
child: Text(
"step_by_step".tr(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.sp,
color: Colors.black,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(
height: 25,
),
const CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.orange),
),
],
),
),
// Second LinearLayout with background image (rotated)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Transform.rotate(
angle: 3.14159, // Rotate 180 degrees (Pi approximation)
child: Container(
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/header_login.png'),
fit: BoxFit.fill,
),
),
),
),
),
// Copyright TextView - horizontally centered
Align(
alignment: Alignment.bottomCenter,
child: Container(
padding: const EdgeInsets.only(bottom: 40.0),
child: Text(
"${'hakan_aydin'.tr()} ${DateTime.now().year}",
style: const TextStyle(
fontSize: 20,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
);
}
Future<void> _checkInternetAndNavigate() async {
bool hasInternet = await checkInternetConnection();
if (hasInternet) {
bool isFirstLaunch = await MySharedPreferences.isFirstLaunch();
if (isFirstLaunch) {
_navigateToPreviewPage();
} else {
_navigateToLoginPage();
}
} else {
_navigateToNetworkErrorPage();
}
}
void _navigateToPreviewPage() async {
Timer(
const Duration(seconds: 3),
() {
MySharedPreferences.setFirstLaunch(false);
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const PreviewPage()),
);
},
);
}
void _navigateToLoginPage() {
Future.delayed(const Duration(seconds: 2)).then((value) {
Navigator.pushReplacement(
context, CupertinoPageRoute(builder: (_) => const LoginScreen()));
});
}
void _navigateToNetworkErrorPage() {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => NetworkErrorPage()),
);
}
void loadBackgroundColor() async {
int colorValue = await MySharedPreferences.getBackgroundColor();
setState(() {
selectedBackgroundColor = Color(colorValue);
});
}
}
İlk olarak, gerekli kütüphaneler ve paketler import
ifadeleriyle projeye dahil edilir. Bu paketler, uygulamanın çalışması için gerekli olan işlevleri sağlar. ThemeMode
adında bir değişken tanımlanır ve varsayılan olarak ThemeMode.dark
değeri atanır. Bu değişken, uygulamanın tema modunu temsil eder.
main
fonksiyonu oluşturulur. Bu fonksiyon, uygulama başlatıldığında çalıştırılacak ilk fonksiyondur.
WidgetsFlutterBinding.ensureInitialized()
çağrısı ile Flutter bağlamı başlatılır. GetStorage.init()
ve EasyLocalization.ensureInitialized()
fonksiyonları çağrılarak, veri depolama ve çoklu dil desteği için gerekli olan paketlerin başlatılması sağlanır.
SystemChrome.setSystemUIOverlayStyle()
çağrısı ile sistem arayüzü overlay stili ayarlanır. Bu durumda, durum çubuğunun şeffaf olması sağlanır. runApp()
fonksiyonu çağrılarak uygulama başlatılır. Bu fonksiyon, uygulamanın kök widget’ını alır ve çalıştırır. EasyLocalization
widget’ı, uygulamanın çoklu dil desteğini sağlar.
EasyLocalization(
supportedLocales: const [
Locale('en'),
Locale('tr'),
Locale('fr'),
Locale('zh'),
],
path: 'assets/translations',
fallbackLocale: const Locale('en'),
child: MyApp()),
Yukarıdaki resimde uygulamanın dil desteği için gerekli olan json dosyaları gösterilmektedir. Burada
tr.json için;
"login_successful": "Giriş Başarılı",
en.json için;
"login_successful": "Login Successful",
fr.json için;
"login_successful": "Connexion réussie",
zh.json için ise;
"login_successful": "登录成功",
MyApp
adında bir sınıf tanımlanır ve StatelessWidget
sınıfından türetilir. Bu sınıf, uygulamanın ana widget’ını temsil eder. build
metodu, MyApp
sınıfının bir parçası olarak tanımlanır. Bu metot, widget’ın görüntüsünü oluşturur ve döndürür. ScreenUtilInit
widget’ı, ekran boyutlarını ve düzenini ayarlar. Bu, farklı ekran boyutlarında ve yoğunluklarında tutarlı bir görünüm sağlar.
MaterialApp
widget’ı, Flutter uygulamasının temel yapılandırmasını sağlar. localizationsDelegates
, supportedLocales
ve locale
parametreleri, uygulamanın dil desteğini yapılandırır. debugShowCheckedModeBanner
parametresi, hata ayıklama bandının görüntülenip görüntülenmeyeceğini belirler.SplashScreen
widget’ı, uygulamanın başlangıç ekranını temsil eder. StatefulWidget
sınıfından türetilir. initState
metodu, SplashScreenState
sınıfının bir parçası olarak tanımlanır ve widget’ın durumunu başlatır. Bu metot, widget oluşturulduğunda bir kez çağrılır. build
metodu, SplashScreenState
sınıfının bir parçası olarak tanımlanır. Bu metot, widget’ın görüntüsünü oluşturur ve döndürür. MySharedPreferences.setLocalName()
çağrısı ile yerel dil ayarları depolanır. SystemChrome.setEnabledSystemUIMode()
çağrısı ile tam ekran modu etkinleştirilir. Scaffold
widget’ı, bir materyal tasarım yapısını temsil eder. backgroundColor
özelliği, widget’ın arka plan rengini belirler.
Future<void> _checkInternetAndNavigate() async {
bool hasInternet = await checkInternetConnection();
if (hasInternet) {
bool isFirstLaunch = await MySharedPreferences.isFirstLaunch();
if (isFirstLaunch) {
_navigateToPreviewPage();
} else {
_navigateToLoginPage();
}
} else {
_navigateToNetworkErrorPage();
}
}
void _navigateToNetworkErrorPage() {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => NetworkErrorPage()),
);
}
Bu kod bloğu, _navigateToNetworkErrorPage()
adında bir fonksiyon tanımlar. Bu fonksiyon, ağ hatası sayfasına yönlendirme işlemini gerçekleştirir. İşlevi şu adımları takip eder. Navigator.pushReplacement()
fonksiyonu çağrılır. Bu fonksiyon, yeni bir sayfaya geçişi sağlar ve önceki sayfayı bellekten kaldırır. context
parametresi, mevcut widgetin işlevsel bağlamını temsil eder. Bu parametre, yönlendirme işlemi için gereklidir. MaterialPageRoute
widget’ı, sayfa geçişini sağlar ve hedef sayfanın oluşturulmasını belirtir. builder
parametresi, hedef sayfanın nasıl oluşturulacağını belirten bir anonim fonksiyonu alır. Bu durumda, NetworkErrorPage
sayfası oluşturulur. Yönlendirme işlemi tamamlanır ve kullanıcı ağ hatası sayfasına yönlendirilir.
network_error.dart dosyası:
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_svg/svg.dart';
import '../../core/ResponsiveDesign.dart';
import '../loginscreen.dart';
class NetworkErrorPage extends StatefulWidget {
@override
State<NetworkErrorPage> createState() => NetworkErrorPageState();
}
class NetworkErrorPageState extends State<NetworkErrorPage> {
bool isOnline = false;
bool isButtonEnabled = true;
bool isLoading = false; // İlerleme çemberinin görünürlüğü
@override
void initState() {
super.initState();
checkInternetConnection();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: Colors.white,
body: Stack(
children: [
// First LinearLayout with background image
Container(
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/header_login.png'),
fit: BoxFit.fill,
),
),
),
// Centered Logo ImageView
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
isOnline
? 'assets/images/network_yes.svg'
: 'assets/images/network_no.svg',
width: 100,
height: 100,
),
// TextView with "step_by_step" text
const SizedBox(
height: 25,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 3.sp),
child: Column(
children: [
Text(
isOnline ? "yes".tr() : "oh_no".tr(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.sp,
color: Colors.black,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
Text(
isOnline
? 'internet_problem_control'.tr()
: "no_internet_connection".tr(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.sp,
color: Colors.black,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
Text(
isOnline ? "" : "or_try_again".tr(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.sp,
color: Colors.black,
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20.h),
SizedBox(
width: 180.w,
height: 50.h,
child: ElevatedButton(
onPressed: () {
showLoadingDialog(
context); // İlerleme çemberi göster
retryProcess(context);
},
style: ButtonStyle(
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(18.sp),
side: BorderSide(color: Colors.pink))),
backgroundColor: MaterialStateColor.resolveWith(
(states) => Colors.pink),
foregroundColor: MaterialStateColor.resolveWith(
(states) => Colors.white)),
child: Text('retry'.tr()),
),
),
],
),
),
const SizedBox(
height: 25,
),
],
),
),
// Second LinearLayout with background image (rotated)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Transform.rotate(
angle: 3.14159, // Rotate 180 degrees (Pi approximation)
child: Container(
height: 200,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/header_login.png'),
fit: BoxFit.fill,
),
),
),
),
),
// Copyright TextView - horizontally centered
],
),
),
);
}
Future<void> retryProcess(BuildContext context) async {
if (!isButtonEnabled) return;
setState(() {
isButtonEnabled = false;
isLoading = true; // İlerleme çemberini göster
});
bool isOnlineNow = await checkInternetConnection();
if (isOnlineNow) {
setState(() {
isOnline = true;
});
Future.delayed(const Duration(seconds: 2), () {
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => const LoginScreen(),
),
);
});
} else {
// İnternet bağlantısı yoksa işlem yapma
setState(() {
isButtonEnabled = true;
isLoading = false; // İlerleme çemberini gizle
});
}
}
Future<void> showLoadingDialog(BuildContext context) async {
showDialog(
context: context,
builder: (BuildContext context) {
return const AlertDialog(
backgroundColor: Colors.white,
shadowColor: Colors.white,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
backgroundColor: Colors.white,
),
SizedBox(height: 10),
Text('Loading...'),
],
),
);
},
);
await Future.delayed(Duration(seconds: 2)); // 2 saniye bekle
Navigator.of(context).pop(); // AlertDialog'ı kapat
}
Future<bool> checkInternetConnection() async {
var connectivityResult = await (Connectivity().checkConnectivity());
return connectivityResult != ConnectivityResult.none;
}
}
Bu kod, Flutter kullanılarak yazılmış NetworkErrorPage
adlı bir sayfayı tanımlar. Bu sayfa, ağ hatası durumunda görüntülenen bir arayüzü içerir. İşlevleri aşağıdaki gibi açıklayabiliriz:
NetworkErrorPage
sınıfı,StatefulWidget
sınıfını genişleterek bir durum sınıfı oluşturur. Bu durum sınıfı, sayfanın dinamik durumunu yönetir.isOnline
,isButtonEnabled
veisLoading
gibi değişkenler, sayfanın durumu için kullanılır. Örneğin,isOnline
değişkeni, internet bağlantısı durumunu temsil eder.initState()
metodu, sayfa oluşturulduğunda çağrılır vecheckInternetConnection()
fonksiyonunu çağırarak internet bağlantısı durumunu kontrol eder.build()
metodu, sayfanın arayüzünü oluşturur ve döndürür. Bu metot içindeSafeArea
veScaffold
widget’ları kullanılır. Arka plan rengi beyaz olan birStack
widget’ı içinde diğer widget’lar yer alır.SvgPicture.asset()
widget’ı, SVG formatında bir resmi görüntüler.isOnline
durumuna bağlı olarak farklı resimler gösterilir.- Metin widget’ları, dil desteği eklemek için
easy_localization
paketinden yararlanır. Metinler,tr()
metodunu kullanarak yerelleştirilir. ElevatedButton
widget’ı, yeniden deneme işlemi için bir düğmeyi temsil eder.onPressed
özelliği, düğmeye basıldığındashowLoadingDialog()
veretryProcess()
fonksiyonlarını çağırır.retryProcess()
fonksiyonu, yeniden deneme işlemini gerçekleştirir. İnternet bağlantısı kontrol edilir ve duruma bağlı olarak ilgili işlemler yapılır. Örneğin, internet bağlantısı varsaLoginScreen
sayfasına yönlendirilir.showLoadingDialog()
fonksiyonu, yükleniyor iletişim kutusunu (AlertDialog
) gösterir.CircularProgressIndicator
widget’ı, ilerleme çemberini temsil eder. Belirli bir süre bekledikten sonra iletişim kutusu kapatılır.checkInternetConnection()
fonksiyonu, internet bağlantısını kontrol eder.Connectivity
sınıfı kullanılarak bağlantı durumu kontrol edilir.
isOnline
? 'assets/images/network_yes.svg'
: 'assets/images/network_no.svg',
Bu kod parçası, bir koşul ifadesi kullanarak isOnline
değişkeninin durumuna bağlı olarak farklı SVG resimlerinin yolunu belirler. 'assets/images/network_yes.svg'
ve 'assets/images/network_no.svg'
, SVG formatında bulunan iki farklı resmin dosya yolunu temsil eder. Bu resimler, internet bağlantısı durumuna göre farklı ikonları veya görselleri temsil edebilir. Koşul ifadesi, isOnline
değişkeninin değerini kontrol eder. Eğer isOnline
true
ise, yani internet bağlantısı varsa, 'assets/images/network_yes.svg'
yolunu temsil eden resim kullanılır. Eğer isOnline
false
ise, yani internet bağlantısı yoksa, 'assets/images/network_no.svg'
yolunu temsil eden resim kullanılır. Bu yöntem sayesinde, internet bağlantısı durumuna göre farklı resimlerin görüntülenmesi sağlanır.
Future<void> showLoadingDialog(BuildContext context) async {
showDialog(
context: context,
builder: (BuildContext context) {
return const AlertDialog(
backgroundColor: Colors.white,
shadowColor: Colors.white,
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(
backgroundColor: Colors.white,
),
SizedBox(height: 10),
Text('Loading...'),
],
),
);
},
);
await Future.delayed(Duration(seconds: 2)); // 2 saniye bekle
Navigator.of(context).pop(); // AlertDialog'ı kapat
}
Bu kod, showLoadingDialog
adlı bir asenkron fonksiyonu tanımlar. Bu fonksiyon, bir yükleniyor iletişim kutusu (AlertDialog
) gösterir, belirli bir süre bekler ve sonra iletişim kutusunu kapatır. İşlevin ana gövdesi showDialog
fonksiyonunu kullanır. showDialog
fonksiyonu, context
parametresiyle birlikte bir builder
işlevi alır. builder
işlevi, iletişim kutusunun içeriğini döndürür. builder
işlevinde, AlertDialog
widget’ı kullanılır. AlertDialog
widget’ının backgroundColor
özelliği beyaz olarak ayarlanır ve gölgelendirme için shadowColor
özelliği de beyaz olarak ayarlanır. İçerik olarak, bir sütun (Column
) widget’ı kullanılır. Bu sütunun içinde, CircularProgressIndicator
widget’ı yüklenme çemberini göstermek için kullanılır. backgroundColor
özelliği de beyaz olarak ayarlanır. Ardından, SizedBox
widget’ı kullanılarak bir boşluk eklenir ve altında bir metin (Text
) widget’ı görüntülenir. Bu metin, “Loading…” olarak ayarlanır. showDialog
fonksiyonu çağrıldığında, iletişim kutusu görüntülenir. Ardından, await Future.delayed(Duration(seconds: 2))
ifadesi kullanılarak 2 saniye beklenir. Bu, iletişim kutusunun belirli bir süre görüntülenmesini sağlar. Son olarak, Navigator.of(context).pop()
ifadesi kullanılarak iletişim kutusu kapatılır. pop()
fonksiyonu, en üstteki arayüz elemanını (burada iletişim kutusunu) kapatır ve altındaki elemanlara geri döner. Bu şekilde, showLoadingDialog
fonksiyonu kullanılarak bir yükleniyor iletişim kutusu gösterilebilir ve belirli bir süre sonra otomatik olarak kapatılabilir.