How to load multiple future in flutter - flutter

I'm creating a flutter application that needs to load email and theme from preferences settings. Loading email will check whether the user is authenticated while loading the theme will check whether the theme is light or dark mode. The problem I'm facing is that dark or light mode is getting applied after the interface has been created. I know that using the future builder can help solve this but I have no idea on how to use it in theme.dart file. Any help please.
theme.dart file
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
CustomTheme customTheme = CustomTheme();
class CustomTheme extends ChangeNotifier {
static bool isDarkTheme = false;
ThemeData get currentTheme => isDarkTheme
? ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: Colors.grey.shade900,
primaryColor: Colors.white,
accentColor: Colors.blue,
iconTheme: const IconThemeData(color: Colors.white, opacity: 0.8),
)
: ThemeData(
brightness: Brightness.light,
scaffoldBackgroundColor: Colors.white,
primaryColor: Colors.black,
accentColor: Colors.blue,
iconTheme: const IconThemeData(color: Colors.black, opacity: 0.8),
dividerColor: Colors.black12,
);
CustomTheme() {
loadPrefs();
}
void toggleTheme() {
isDarkTheme = !isDarkTheme;
savePrefs();
notifyListeners();
}
void savePrefs() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
_prefs.setBool('theme', isDarkTheme);
}
void loadPrefs() async {
SharedPreferences _prefs = await SharedPreferences.getInstance();
isDarkTheme = _prefs.getBool('theme') ?? false;
notifyListeners();
}
}
main.dart file
import 'package:chatapp/home.dart';
import 'package:chatapp/login.dart';
import 'package:chatapp/themes.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
Future<String> getSharedPrefs() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String _email = prefs.getString('email') ?? '';
return _email;
}
#override
void initState() {
super.initState();
customTheme.addListener(() {
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: customTheme.currentTheme,
home: FutureBuilder<String>(
future: getSharedPrefs(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
} else if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData && snapshot.data!.isNotEmpty) {
return Home();
} else {
return Login();
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
);
}
}

Try below code hope its helpful to you.used Future.wait([]) method for that refer here
FutureBuilder(
future: Future.wait([
getSharedPrefs(),
// call your extra future method here
]),
builder:
),

Related

Flutter switch animation is not showing in Alert Dialog

I have an alert dialog with a switch. If I press the switch, the boolean changes like I want it to (I have it set so the homepage background color changes based on the bool), but the switch animation does not register until I close out of the alert dialog and re-open it again. I tried wrapping it in a StatefulBuilder like I saw in another post, but in that case, the animation works but the bool does not change. I am trying to share this bool value throughout my app, so I'm using Shared Preferences.
here is the full code
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool bulb = false;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: bulb ? Colors.white : Colors.blue,
appBar: AppBar(actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
content: Column(children: [
Switch(
value: bulb,
onChanged: (bool isChecked) async {
final prefs = await SharedPreferences.getInstance();
setState(() {
bulb = isChecked;
prefs.setBool('bulb', isChecked);
});
},
),
StatefulBuilder(builder:
(BuildContext context, StateSetter setState) {
return Column(children: [
Switch(
value: bulb,
onChanged: (bool isChecked) async {
final prefs =
await SharedPreferences.getInstance();
setState(() {
bulb = isChecked;
prefs.setBool('bulb', isChecked);
});
},
),
]);
})
])));
})
]),
body: Center(),
);
}
}
Solution:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool bulb = false;
Future openDialog() => showDialog(
context: context,
builder: (context) => StatefulBuilder(
builder: (context, setState) => AlertDialog(
content: Column(children: [
Switch(
value: bulb,
onChanged: (bool isChecked) async {
final prefs = await SharedPreferences.getInstance();
setState(() {
bulb = isChecked;
prefs.setBool('bulb', isChecked);
});
},
),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("Save")),
]))));
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: bulb ? Colors.white : Colors.blue,
appBar: AppBar(actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: () async {
await openDialog();
setState(() {});
}),
]),
body: Center(),
);
}
}
You have to use the StatefulBuilder itself.
Now, when you are calling setState inside the dialog, only the state inside the dialog is changing. The state of the screen remains the same. This is why switch changed animation is happening but the bulb value in the screen is not changing.
The workaround is, you can use a callback function to call setState in the screen too, when the value of switch changes.

Flutter Themedata Refrector

How can write readable and clean code flutter ThemeData for light mode and dark mode.
I already wrote but that is not readable how should i write for themedata for lightmode and darkmode readable, maintain and clearly?
You can easily store your theme preference in form of a string and then at the start of your app check if there is value stored on file system, if so apply that theme as shown below.
StorageManager.dart
import 'package:shared_preferences/shared_preferences.dart';
class StorageManager {
static void saveData(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
if (value is int) {
prefs.setInt(key, value);
} else if (value is String) {
prefs.setString(key, value);
} else if (value is bool) {
prefs.setBool(key, value);
} else {
print("Invalid Type");
}
}
static Future<dynamic> readData(String key) async {
final prefs = await SharedPreferences.getInstance();
dynamic obj = prefs.get(key);
return obj;
}
static Future<bool> deleteData(String key) async {
final prefs = await SharedPreferences.getInstance();
return prefs.remove(key);
}
}
Define your theme properties in a theme variable like below and initialize your _themedata variable on the basis of value inside storage.
ThemeManager.dart
import 'package:flutter/material.dart';
import '../services/storage_manager.dart';
class ThemeNotifier with ChangeNotifier {
final darkTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.black,
brightness: Brightness.dark,
backgroundColor: const Color(0xFF212121),
accentColor: Colors.white,
accentIconTheme: IconThemeData(color: Colors.black),
dividerColor: Colors.black12,
);
final lightTheme = ThemeData(
primarySwatch: Colors.grey,
primaryColor: Colors.white,
brightness: Brightness.light,
backgroundColor: const Color(0xFFE5E5E5),
accentColor: Colors.black,
accentIconTheme: IconThemeData(color: Colors.white),
dividerColor: Colors.white54,
);
ThemeData _themeData;
ThemeData getTheme() => _themeData;
ThemeNotifier() {
StorageManager.readData('themeMode').then((value) {
print('value read from storage: ' + value.toString());
var themeMode = value ?? 'light';
if (themeMode == 'light') {
_themeData = lightTheme;
} else {
print('setting dark theme');
_themeData = darkTheme;
}
notifyListeners();
});
}
void setDarkMode() async {
_themeData = darkTheme;
StorageManager.saveData('themeMode', 'dark');
notifyListeners();
}
void setLightMode() async {
_themeData = lightTheme;
StorageManager.saveData('themeMode', 'light');
notifyListeners();
}
}
Wrap your app with themeProvider and then apply theme using consumer. By doing so whenever you change the value of theme and call notify listeners widgets rebuild to sync changes.
Main.dart
void main() {
return runApp(ChangeNotifierProvider<ThemeNotifier>(
create: (_) => new ThemeNotifier(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<ThemeNotifier>(
builder: (context, theme, _) => MaterialApp(
theme: theme.getTheme(),
home: Scaffold(
appBar: AppBar(
title: Text('Hybrid Theme'),
),
body: Row(
children: [
Container(
child: FlatButton(
onPressed: () => {
print('Set Light Theme'),
theme.setLightMode(),
},
child: Text('Set Light Theme'),
),
),
Container(
child: FlatButton(
onPressed: () => {
print('Set Dark theme'),
theme.setDarkMode(),
},
child: Text('Set Dark theme'),
),
),
],
),
),
),
);
}
}
Here is the link to github repository.

Flutter Conditional Route with Auto Route not working

i want to implement conditional routing with auto route package
isLoggedIn returns true but
autoroute redirects to login page,
how can i resolve this problem
import 'package:animated_splash_screen/animated_splash_screen.dart';
import 'package:bot_toast/bot_toast.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:expense/config/routes/app_router.dart';
import 'package:expense/config/themes/theme_config.dart';
import 'package:expense/constants/assets_path_constants.dart';
import 'package:expense/constants/constants.dart';
import 'package:expense/core/auth/login/bloc/cubit/auth_cubit.dart';
import 'package:expense/core/auth/login/repository/mongodb/mongo_auth_repository.dart';
import 'package:expense/utils/services/lang/language_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'core/splash_screen.dart';
Future<void> main() async {
statusBar();
await _init();
runApp(
EasyLocalization(
supportedLocales: LanguageService.instance.supportedLocales,
path: AssetsPath.langAssetPath,
child: const Expense(),
),
);
}
void statusBar() {
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: ColorConstants.backgroundColor,
systemNavigationBarIconBrightness: Brightness.dark,
),
);
}
Future<void> _init() async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
}
class Expense extends StatefulWidget {
const Expense({Key? key}) : super(key: key);
#override
State<Expense> createState() => _ExpenseState();
}
class _ExpenseState extends State<Expense> {
final _appRouter = AppRouter();
bool isLoggedIn = false;
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AuthCubit(MongoAuthRepository()),
child: BlocConsumer<AuthCubit, AuthState>(
builder: (context, state) {
context.read<AuthCubit>().isValid();
return _buildMaterialApp(context, state);
},
listener: (context, state) {
if (state is TokenIsValid) {
isLoggedIn = state.isValid;
}
},
),
);
}
MaterialApp _buildMaterialApp(BuildContext context, AuthState state) {
print(isLoggedIn);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Gider',
// Tost Mesajı Init
builder: BotToastInit(),
theme: CustomTheme.primaryTheme,
localizationsDelegates: context.localizationDelegates,
supportedLocales: context.supportedLocales,
locale: context.locale,
home: AnimatedSplashScreen(
splash: const SplashPage(),
animationDuration: const Duration(seconds: 2),
nextScreen: MaterialApp.router(
debugShowCheckedModeBanner: false,
theme: CustomTheme.primaryTheme,
routeInformationParser: _appRouter.defaultRouteParser(includePrefixMatches: true),
routerDelegate: _appRouter.delegate(
initialRoutes: [
if (isLoggedIn) HomeRoute(response: null) else const LoginRoute(),
],
),
),
),
);
}
}

Shared Preferences key value not changing

I want to write code that directs the user to a welcome page if it's the first time the app is being run. After the user logs in, any subsequent launches of the app direct the user to log in, skipping the welcome page. It seems that when I try to set the value of my key upon logging in, it's not changing, because after logging in, closing the app and launching it again it's still going to the welcome page. How do I fix this? I'm not sure whether the issue is with the initialisation or the setting of the value upon login.
Here's the initialisation of my app:
import 'package:screens/welcomepages/signup_or_login.dart';
import 'package:screens/welcomepages/welcome.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:config/theme.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.dark));
runApp(MyApp(prefs: await _prefs));
}
class MyApp extends StatefulWidget {
const MyApp({Key? key, this.title, required this.prefs})
: super(key: key);
final String? title;
final SharedPreferences prefs;
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late SharedPreferences prefs;
#override
void initState() {
super.initState();
initializePreference();
}
Future<void> initializePreference() async {
prefs = await SharedPreferences.getInstance();
setState(() {
prefs.setBool("hasLoggedIn", false);
});
}
#override
Widget build(BuildContext context) {
if (prefs.getBool("hasLoggedIn") == false) {
return MaterialApp(
theme: theme(),
debugShowCheckedModeBanner: false,
home: Welcome(prefs: prefs),
);
}
return MaterialApp(
theme: theme(),
debugShowCheckedModeBanner: false,
home: SignUpOrLogIn(prefs: prefs),
);
}
}
And here's the relevant parts of my log in page
import 'package:services/auth/auth_service.dart';
import 'package:widgets/text_fields_widgets/email_textfield.dart';
import 'package:widgets/text_fields_widgets/password_textfeild.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Login extends StatefulWidget {
const Login({Key? key, required this.prefs}) : super(key: key);
final SharedPreferences prefs;
#override
_LoginState createState() => _LoginState(prefs);
}
class _LoginState extends State<Login> {
late final SharedPreferences prefs;
_LoginState(SharedPreferences prefs);
Future<void> initializePreference() async {
prefs = await SharedPreferences.getInstance();
}
#override
void initState() {
super.initState();
initializePreference().whenComplete(() {
setState(() {
prefs.getBool("isFirstRun");
});
});
}
#override
Widget build(BuildContext context) {
Material btnLogin = loginBTN(context);
return Scaffold(
.....
Padding(
padding: const EdgeInsets.all(8.0),
child: btnLogin,
)
}
Material loginBTN(BuildContext context) {
// ignore: unused_local_variable
final btnLogin = Material(
elevation: 5,
borderRadius: BorderRadius.circular(30),
color: Theme.of(context).primaryColor,
child: MaterialButton(
padding: const EdgeInsets.fromLTRB(20, 15, 20, 15),
minWidth: MediaQuery.of(context).size.width,
onPressed: () async {
setState(() {
prefs.setBool("hasLoggedIn", true);
});
setState(
() => loadingAnimation = true,
);
await Future.delayed(const Duration(seconds: 1));
setState(
() => loadingAnimation = false,
);
await Authservice().logInMethod(
emailController.text, passwordController.text, context, _formKey);
},
child: loadingAnimation
? const CircularProgressIndicator(
color: Colors.white,
)
: const Text(
"Log in",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 24, color: Colors.white),
),
),
);
return btnLogin;
}
}
Sorry for how lengthy this is, tried to shorten it to only provide what's relevant.
Your are calling initializePreference() inside initState() and that function set hasLoggedIn to false. So even if you set it to true in your login page, when restarting the app it will be set again to false.
Try this brother
if (prefs.getBool("hasLoggedIn") == null || prefs.getBool("hasLoggedIn") == false) {
return MaterialApp(
theme: theme(),
debugShowCheckedModeBanner: false,
home: Welcome(prefs: prefs),
);
}

Using Flutter and Dart, how can I get this Future to work?

I know that there are other ways of achieving this, but I want to use a Future via initState() to obtain a List using SharedPreferences. The following illustrates what I want to achieve, but it does not work as required, because the Future returns immediately before completing the task.
How should this be structured?
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class _MyHomePageState extends State<MyHomePage> {
SharedPreferences _prefs;
String _sMessage = "No response from getting params";
List<String> _lsCategories;
#override
void initState() {
super.initState();
_initPreferences().then((_) {
try {
_lsCategories = _prefs.getStringList("categories") ?? [];
debugPrint("Categories = $_lsCategories");
_sMessage = "Categories loaded OK";
} catch (vError) {
_sMessage = ("Error loading categories = $vError");
}
setState(() {});
});
}
Future<void> _initPreferences() async {
SharedPreferences.getInstance().then((prefs) {
_prefs = prefs;
debugPrint("Preferences initialized OK");
}).catchError((vError) {
debugPrint("Error initializing preferences = ${vError.toString()}");
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_sMessage,
style: TextStyle(
color: Colors.red[500],
fontWeight: FontWeight.bold,
fontSize: 20)),
],
),
),
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Future Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: "Flutter Future Test"),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
Change your _initPreferences method to the following:
Future<void> _initPreferences() async {
try {
final prefs = await SharedPreferences.getInstance();
_prefs = prefs;
debugPrint("Preferences initialized OK");
} catch (e) {
debugPrint("Error initializing preferences = ${e.toString()}");
}
}
The issue is that when using .then() on a Future you are not waiting for that future to finish, using await does wait.
Try it.
Future<void> _initPreferences() async {
_prefs = await SharedPreferences.getInstance().catchError((vError) {
debugPrint("Error initializing preferences = ${vError.toString()}");
};
debugPrint("Preferences initialized OK");
}