How to save SharedPreferences as an boolean flutter (problem) - flutter

I try to change theme by try to use SharedPreferences to save the data. So when I reopen the app it doesn’t reset. But the problem is when I reopen the app it does reset every-time.
Please look though my code
and maybe point out what’s wrong or provide some code if you’ve already knows. Thanks
In theme:
bool? colorMode = true;
Future<bool> savebool(bool value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("colorMode", true);
return colorMode!;
}
Future<bool> loadbool() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.getBool("colorMode")!;
return colorMode!;
}
InkWell(
child: Row(mainAxisSize: MainAxisSize.min, children: const [
SizedBox(
width: 30.0,
height: 60.0,
),
Text('- Dark Mode', style: TextStyle()),
]),
onTap: () => {
themeManager.themeMode == ThemeMode.dark,
setState(
() {
themeManager.toggleTheme(colorMode!);
colorMode = colorMode;
savebool(colorMode!);
},
),
},
),
In main (some):
ThemeManager themeManager = ThemeManager();
#override
void initState() {
themeManager.addListener(themeListener);
super.initState();
}
#override
void dispose() {
themeManager.removeListener(themeListener);
super.dispose();
}
themeListener() {
if (mounted) {
setState(() {});
}
}
MultiProvider(
providers: [
Provider(create: (_) => User),
ChangeNotifierProvider(create: (context) => themeManager)
],
child: MaterialApp(
title: 'My app',
themeMode: themeManager.themeMode,
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
home: const LoginScreen(),
debugShowCheckedModeBanner: false,
),
);
}
}

I think you forgot to set colorMode in loadbool() function...
Future<bool> loadbool() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
///right here, i think it must be like this
colorMode = prefs.getBool("colorMode") ?? false;
return colorMode!;
}

Your codes aren't full enough. I can't see the way you use save/load function. Anyway, before you get value from shared preference, you have to check it if it is null. I often load shared preferences in my singleton, like "isLogin", "firstOpenApp", "token"... Example:
DataInstance().firstLogin = prefs.getBool(PreferenceConstant.PREF_KEY_FIRST_LOGIN) ?? true;

Related

How to get and use saved language/locale from shared preferences in Flutter?

I am using flutter localizations for changing language in my flutter app. I want to change my app's language in real time and have implemented logic for that. Now, I want that when user closes app and restarts it, he gets same language he chose before, i.e. language should not set back to default after user closes the app. For this purpose, I am using shared preferences to save the code of language that user selected, and now I can't retrieve it in the beginning of the app. Please help!
locale_provider.dart:
import 'package:flutter/material.dart';
import 'package:myapp/l10n/l10n.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LocaleProvider extends ChangeNotifier {
Locale? _locale = const Locale('en');
Locale? get locale => _locale;
void setLocale(Locale locale) {
if (!L10n.all.contains(locale)) return;
_locale = locale;
notifyListeners();
//setLocaleSettings(locale);
}
void clearLocale() {
_locale = null;
notifyListeners();
}
void changeLocaleSettings(Locale newLocale) async {
if(newLocale == Locale('en')) {
_locale = Locale('en');
} else if(newLocale==Locale('uk')){
_locale = Locale('uk');
} else if(newLocale==Locale('ru')){
_locale = Locale('ru');
}
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("code", _locale?.countryCode??"en");
notifyListeners();
}
Future getLocaleFromSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String code = prefs.getString("code")??"en";
Locale newLocale = Locale(code);
if(newLocale == Locale('en')) {
_locale = Locale('en');
} else if(newLocale==Locale('uk')){
_locale = Locale('uk');
} else if(newLocale==Locale('ru')){
_locale = Locale('ru');
}
}
}
In my language selection dropdown, I am changing language like this:
class LanguagePickerWidget extends StatelessWidget {
const LanguagePickerWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final provider = Provider.of<LocaleProvider>(context);
final locale1 = provider.locale ?? const Locale('en');
return DropdownButtonHideUnderline(
child: SizedBox(
width: 15,
child: Theme(
data: Theme.of(context).copyWith(
canvasColor: Colors.green.shade300,
),
child: DropdownButton(
borderRadius:BorderRadius.circular(12),
isExpanded: true,
itemHeight: null,
value: locale1,
icon: Container(
//width: 10.0
),
items: L10n.all.map(
(locale) {
final flag = L10n.getFlag(locale.languageCode);
return DropdownMenuItem(
child: Align(
alignment: Alignment.center,
child: Text(
flag,
style: const TextStyle(fontSize: 22.0),
),
),
value: locale,
onTap: () {
final provider = Provider.of<LocaleProvider>(context, listen: false);
provider.setLocale(locale);
provider.changeLocaleSettings(locale);
print(locale);
},
);
},
).toList(),
onChanged: (_) {},
),
),
),
);
}
}
In main.dart:
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => ChangeNotifierProvider(
create: (context) => LocaleProvider(),
builder: (context, child) {
final provider = Provider.of<LocaleProvider>(context);
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
scaffoldBackgroundColor: Colors.lightGreen[100],
primarySwatch: Colors.green,
),
//locale: provider.locale,
supportedLocales: L10n.all,
localizationsDelegates: [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
home: const HomePage(),
);
});
}
It works pretty well for changing the language in runtime..
And now I don't understand how to retrieve and set the previously selected language (from shared preferences)? Please help!
Create a default constructor for LocaleProvider class and put this code inside it
LocaleProvider(){
Fututre.delayed(Duration.zero, getLocaleFromSettings);
}
and at the end of your function getLocaleFromSettings(), call notifyListeners();
the reason to use FutureBuilder is that if you called notifyListeners while building frame is processing it will give you an error, so adding some delay to prevent rendering issues
tried this get locale whenever you intialize LocaleProvider class:-
import 'package:flutter/material.dart';
import 'package:myapp/l10n/l10n.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LocaleProvider extends ChangeNotifier {
Locale? locale;
LocaleProvider({this.locale});
Locale? get locale => _locale;
void setLocale(Locale locale) {
if (!L10n.all.contains(locale)) return;
_locale = locale;
notifyListeners();
//setLocaleSettings(locale);
}
void clearLocale() {
_locale = null;
notifyListeners();
}
void changeLocaleSettings(Locale newLocale) async {
if(newLocale == Locale('en')) {
_locale = Locale('en');
} else if(newLocale==Locale('uk')){
_locale = Locale('uk');
} else if(newLocale==Locale('ru')){
_locale = Locale('ru');
}
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("code", _locale?.countryCode??"en");
notifyListeners();
}
Future getLocaleFromSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String code = prefs.getString("code")??"en";
Locale newLocale = Locale(code);
if(newLocale == Locale('en')) {
_locale = Locale('en');
} else if(newLocale==Locale('uk')){
_locale = Locale('uk');
} else if(newLocale==Locale('ru')){
_locale = Locale('ru');
}
}
}
use this in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences _sharedPreferences = await SharedPreferences.getInstance();
string savedLangugae = get from sharepreference;
runApp(MultiProvider(providers: [
ChangeNotifierProvider(create: (_) => LocaleProvider(locale:Locale(savedLanguage))),
], child: const MyApp()));
}
Hope it will work.

Flutter console won't show errors

Flutter no longer shows any error message, I'm using android studio, but even if I start the program in console messages still won't appear. For example if mapping an object goes wrong, there will be no error shown in console, I'll have to find it my self
This is my main file:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
ErrorWidget.builder = (FlutterErrorDetails details) => Container(
color: Colors.white,
child: const Center(
child: Text('Error'),
),
);
await SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp],
);
try {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
} catch (e) {}
setupLocator();
await SentryFlutter.init((SentryFlutterOptions options) {
options.reportPackages = false;
options.enableOutOfMemoryTracking = true;
options.enableAppLifecycleBreadcrumbs = false;
options.anrEnabled = true;
options.debug = true;
options.dsn ='';
options.tracesSampleRate = 1.0;
}, appRunner: () => runApp(MyApp(route: route,)));
}
class MyApp extends StatelessWidget {
final String route;
final bool isLoggedIn;
MyApp({
required this.route,
required this.isLoggedIn,
});
#override
Widget build(BuildContext context) {
return GlobalBlocProviders(
isLoggedIn: isLoggedIn,
child: BlocListener<NotificationsBloc, NotificationsState>(
listener: (context, state) {
final route = state.route;
if (route == null) return;
locator<NavigationService>().navigateTo(route);
},
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: TylerTheme,
builder: (BuildContext context, Widget? childWidget) {
return MediaQuery(
data: MediaQuery.of(context).copyWith(
alwaysUse24HourFormat: true,
),
child: childWidget!,
);
},
initialRoute: route,
navigatorObservers: [
StackedService.routeObserver,
SentryNavigatorObserver()
],
navigatorKey: StackedService.navigatorKey,
onGenerateRoute: StackedRouter().onGenerateRoute,
),
),
);
}
}
Would be perfect if you have any suggestions. Thank you!
You just have to print the error via print method.
try {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
} catch (e) {
print("Catch Exception is $e");
}

How can I get preferences from shared preferences in a method that can't by async in Flutter?

I want to get the bool of a shared pref to decide which Widget should get loaded, but the method cant be async or to bool cant get the value because it is not allowed to "await" the value. I have tried fixing it, but it mostly fails because "home" can't receive a future widget..., is there another way how I could do this?
void main() => runApp(MyApp());
setloginbool() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("savelogin", true);
}
Future<bool> getloginbool() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool savelogin = prefs.getBool("savelogin") ?? false;
return savelogin;
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'KHS Plan',
theme: (ThemeData(
textTheme: const TextTheme(
bodyText1: TextStyle(fontSize: 14)
)
)),
home: checkifpassword(),
);
}
}
Widget checkifpassword() {
bool s = await getloginbool();
if(s){
return const Login();
} else {
return const MyHomePage();
}
}
//This does not work as well
checkifpassword() async {
bool s = await getloginbool();
if(s){
return const Login();
} else {
return const MyHomePage();
}
}
You can use FutureBuilder on Home
Future<bool> checkifpassword() async {
//perfrom your async operation and return bool
return await Future.delayed(Duration(seconds: 2), () {
return true;
});
}
And home
home: FutureBuilder<bool>(
future: checkifpassword(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data!) {// for true
return Login();;
} else return MyHomePage();
}
/// check others state
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
},
)

stop closing showDialogue itself after 15 seconds

I am trying to display an information dialog when starting an application. After closing, another window appears asking for permission. I call it all in the initState function. It works, but I noticed that this first info dialog also closes on its own when 15 seconds have elapsed. How do I fix this? So that while the dialog is not closed by the user, the application will not be loaded further?
class _MyAppState extends State<MyApp> {
final keyIsFirstLoaded = 'is_first_loaded';
#override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final context = MyApp.navKey.currentState.overlay.context;
await showDialogIfFirstLoaded(context);
await initPlatformState();
});
}
showDialogIfFirstLoaded(BuildContext context, prefs) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoaded = prefs.getBool(keyIsFirstLoaded);
if (isFirstLoaded == null) {
return showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Dialog
return new AlertDialog(
// title: new Text("title"),
content: new Text("//"),
actions: <Widget>[
new FlatButton(
child: new Text(".."),
onPressed: () {
Navigator.of(context).pop();
prefs.setBool(keyIsFirstLoaded, false);
},
),
],
);
},
);
}
}
initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
print('Initialization done');
final _isRunning = await BackgroundLocator.isRegisterLocationUpdate();
setState(() {
isRunning = _isRunning;
});
onStart();
print('Running ${isRunning.toString()}');
}
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
// ... app-specific localization delegate[s] here
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
navigatorKey:MyApp.navKey,
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: analytics),
],
debugShowCheckedModeBanner: false,
title: '',
theme: ThemeData(),
home: new SplashScreen(),}
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => new _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
Timer _timer;
bool _visible = true;
startTime() async {
_timer = Timer(new Duration(seconds: 5), navigationPage);
}
void navigationPage() {
Navigator.of(context).pushReplacementNamed('/home');
}
#override
void initState() {
_timer = Timer(Duration(seconds: 4),
() => setState(
() {
_visible = !_visible;
},
),
);
startTime();
super.initState();
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
Container(
width: double.infinity,
child: Image.asset('images/bg.jpg',
fit: BoxFit.cover,
height: 1200,
),
),
Container(
width: double.infinity,
height: 1200,
color: Color.fromRGBO(0, 0, 0, 0.8),
),
Container(
alignment: Alignment.center,
child: Row(
children: <Widget>[
Expanded(
flex: 2,
child: Container(
child: Text(''),
),
),
],
),
),
],
);
}
}
This code displays an Alert dialogue if the user is new and after the button click, it will direct him to another dialogue.
I have tested the code and it doesn't close after 15 seconds. I'm still not sure what you're trying to accomplish but I hope this helps.
Init State
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await dialog1(context);
//await initPlatformState();
});
super.initState();
}
Alert Dialog 1
dialog1(BuildContext context)async{
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoaded = prefs.getBool("keyIsFirstLoaded")??true;
if (isFirstLoaded) {
showDialog(
barrierDismissible: false, //disables user from dismissing the dialog by clicking out of the dialog
context: context, builder: (ctx) {
return AlertDialog(
title: Text("dialog 1"), content: Text("Content"), actions: [
TextButton(
child: new Text(".."),
onPressed: () async{
Navigator.pop(ctx);
await dialog2(context);
prefs.setBool("keyIsFirstLoaded", false);
},
),],);
},);
}else{
//not first time
}
}
Alert Dialog 2
void dialog2(BuildContext context)async{
print("dialog 2");
showDialog(context: context, builder: (context) {
return AlertDialog(title: Text("Dialog 2"),content: Text("permissions"),actions: [
TextButton(
child: new Text("close"),
onPressed: () async{
Navigator.pop(context);
//await dialog1(context); //uncomment if you want to go back to dialoge 1
},
),],);
},);
}
You can return a value from Navigator in the first dialog
Navigator.of(context).pop(true);
prefs.setBool(keyIsFirstLoaded, false);
Once it receive true, then only call the second method.
var value = await showDialogIfFirstLoaded(context);
if(value == true) {
await initPlatformState();
}

user still logged in after logging out

I was making an app using flutter and i have used shared_preferences package, and in auth stage i am facing an issue where when i build app user is logged in and when i log out and restart the app after killing it ,it still goes on homepage ,
Here is my code
main.dart
bool checkingKey;
Future<bool> checkKey() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool checkingKey=prefs.containsKey("jwt");
print("$checkingKey");
return checkingKey;
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
Paint.enableDithering = true;
await checkKey().then((value){
checkingKey=value;
});
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
// bool check=checkKey().then((bool value) => true);
print("hello=$checkingKey");
return MaterialApp(
home: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
color: Color(0xffccffcc),
child:checkingKey==false?LoginPage():mainPage()
),
),
),
routes: <String,WidgetBuilder>{
'/home':(BuildContext context)=>mainPage(),
'/login':(BuildContext context)=>LoginPage(),
}
);
}
}
login_signup_Auth.dart
Future<void> attemptLogIn(String username, String password,BuildContext context) async {
///?final storage =parent_inherit.of(context);
///?var verify=storage.verify;
SharedPreferences prefs = await SharedPreferences.getInstance();
print("$username $password");
final http.Response res = await http.post(
"https://green-earth.herokuapp.com/signin",
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
// 'authorization':'Bearer '+
},
body: jsonEncode(<String, String>{
"email": username,
"password": password
}),
);
if(res.statusCode == 200) {
prefs.setString('jwt',res.body);
var value=prefs.getString('jwt');
print("storage= ${value.isEmpty}");
Navigator.of(context).pushNamed('/home');
}
else{
return _showMyDialoglogin(context,res.statusCode);
}
}
void logoutOutOfApp(BuildContext context) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.clear();
Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route<dynamic> route) => false);
}
On the second build without changing anything , the checking key variable is returned 'true' which i don't know ,how can it be possible!!!!!!
I am not getting what i am doing wrong ,also if u see any other problem which can make program efficient or any other code which shall be used .please tell
ThankYou very much!!
Why you are complicating things ?
Your main.dart can simply looks like this
bool checkingKey;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Paint.enableDithering = true;
var prefs = await SharedPreferences.getInstance();
checkingKey = prefs.containsKey("jwt");
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("hello=$checkingKey");
return MaterialApp(
home: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
),
child: Scaffold(
resizeToAvoidBottomInset: false,
body: Container(
color: Color(0xffccffcc),
child: !checkingKey ? LoginPage() : mainPage(),
),
),
),
routes: <String,WidgetBuilder>{
'/home':(BuildContext context) => mainPage(),
'/login':(BuildContext context) => LoginPage(),
},
);
}
}