First I created the GetxController class
final languageController = GetStorage();
var myLocal = [];
void saveLocale(List list) {
languageController.write('savedLocale', list);
}
#override
void onInit() {
List<dynamic>? savedLocale = languageController.read('savedLocale');
if (savedLocale != null) {
myLocal = savedLocale;
}
super.onInit();
}
}
Then I initialized GetStorage in main.dart
final myLocal = LanguageController().myLocal;
void main() async {
print(myLocal);
await GetStorage.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
translations: LocaleString(),
locale: myLocal.isNotEmpty
? Locale(myLocal[0], myLocal[1])
: Locale('en', 'US'),
debugShowCheckedModeBanner: false,
home: HomeScreen(),
);
}
}
And then in the dialog after setting the locale I writes it in storage
Future<dynamic> myMaterialDialog(BuildContext context) {
final LanguageController languageController = Get.find();
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(chooseLanguage.tr),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
onPressed: () {
Get.back();
Get.updateLocale(Locale('en', 'US'));
languageController.saveLocale(['en', 'US']);
},
child: Text(englishLanguage.tr),
),
TextButton(
onPressed: () {
Get.back();
Get.updateLocale(Locale('ru', 'RU'));
languageController.saveLocale(['ru', 'RU']);
},
child: Text(russianLanguage.tr),
),
],
),
);
});
}
And it's not working, every time I restarted my app it's shows 1 what myLocale is empty
To check if saveLocale() method is working, I created printSavedLocale() method
void printSavedLocale() {
print(languageController.read('savedLocale'));
}
and put it to dialoge button after saveLocale() and it's printing my saved locale, but after restarting saved locale is null
use this static methods. put them anywhere in your project:
void setData(String key, dynamic value) => GetStorage().write(key, value);
int? getInt(String key) => GetStorage().read(key);
String? getString(String key) => GetStorage().read(key);
bool? getBool(String key) => GetStorage().read(key);
double? getDouble(String key) => GetStorage().read(key);
dynamic getData(String key) => GetStorage().read(key);
void clearData() async => GetStorage().erase();
I can solove this by reading from the storage directly from main.dart
final LanguageController languageController = Get.put(LanguageController());
final myLocal = LanguageController().readSavedLocale();
void main() async {
await GetStorage.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
translations: LocaleString(),
locale: myLocal.isNotEmpty
? Locale(myLocal[0], myLocal[1])
: Locale('en', 'US'),
debugShowCheckedModeBanner: false,
home: HomeScreen(),
);
}
}
And readSavedLocale() method is
List readSavedLocale() {
var savedLocale = languageController.read('savedLocale');
return savedLocale;
}
if you still needs this , I use my app differently but I just made it work he it my main file (minus what you don' need)
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
await firebaseInitialization.then((value) {
Get.put(HomeController());
});
runApp(Start());
}
class Start extends StatelessWidget {
Start({
Key? key,
}) : super(key: key);
final storage = GetStorage();
#override
Widget build(BuildContext context) {
Get.put(HomeController());
print(storage.read('langCode'));
print(storage.read('countryCode'));
return GetMaterialApp(
translations: LocaleString(),
fallbackLocale: const Locale('en', 'US'),
locale: storage.read('langCode') != null
? Locale(storage.read('langCode'), storage.read('countryCode'))
: const Locale('ar', 'MA'),
title: 'title'.tr,
}));
}
}
i have a button on my drawer that switches between arabic and english, you can put it wherever you want, you just need to have the widget
class Page extends GetView<HomeController>
which gives you the value 'controller' to represent the controller responsible for the language.
and this is the button responsible for the switch:
SizedBox(
height: 70,
child: OutlinedButton(
child: ListTile(
title: Text(
'language'.tr,
style: Theme.of(context).textTheme.headline6,
textDirection: TextDirection.rtl,
),
leading: const Icon(Icons.language),
),
onPressed: () {
controller.switchLang();
},
)),
and here is my homeController which is responsible for the locale:
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
class HomeController extends GetxController {
static HomeController instance = Get.find();
final storage = GetStorage();
var ar = const Locale('ar', 'MA');
var us = const Locale('en', 'US');
switchLang() {
if (Get.locale == us) {
Get.updateLocale(ar);
storage.write('langCode', 'ar');
storage.write('countryCode', 'MA');
} else {
Get.updateLocale(us);
storage.write('langCode', 'en');
storage.write('countryCode', 'US');
}
update();
}
}
in your case if you have multiple locales , just change my switchlang function to handle multiple locales, you can do that easily with a switch case
Related
I have a serious problem with my Riverpod. Specifically, I am using StateProvider in Riverpod package. But when I update state, the widget tree does not rebuild. I checked the new state whether is updated by printing out state to see, I see that they are actually updated.
I have some same situations but when I click hot restart/reload page/scroll up,down mouse to change size chrome window, the widget tree rebuild one time.
Please help me and explain everything the most detail and easy to understand. Thank you very much
new state print out but UI not update
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = new Data(data: rawString);
ref.watch(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider.notifier).state;
final dataText = data?.data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [
Text(dataText)
]
)
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
));
}
}
I don't want to use other pattern as Provider, Bloc, StateNotifierProvider, ChangeNotifierProvider... I only want to run StateProvider successfully. I have refered to many articles and stackoverflows answer but I did't found any useful helps to my case.
final data = ref.watch(helloWorldProvider.notifier).state;
is watching the notifier, which rarely changes. You want to watch the state change, as in:
final data = ref.watch(helloWorldProvider);
Fixed, Tested your code.
I recommend this article Flutter Riverpod 2.0: The Ultimate Guide to Advanced Riverpod 😀
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = Data(data: rawString);
ref.read(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(
List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider)?.data;
final dataText = data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [Text(dataText)]),
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
),
);
}
}
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:developer' as devtools;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocProvider(
create: (context) => PersonsBloc(),
child: const MyHomePage(),
),
);
}
}
#immutable
abstract class LoadAction {
const LoadAction();
}
#immutable
class LoadPersonsAction implements LoadAction {
final PersonsUrl url;
const LoadPersonsAction({required this.url}) : super();
}
enum PersonsUrl {
persons1,
persons2,
}
extension Subscript<T> on Iterable<T> {
T? operator [](int index) => length > index ? elementAt(index) : null;
}
extension UrlString on PersonsUrl {
String get getUrl {
switch (this) {
case PersonsUrl.persons1:
devtools.log("Getting person1 url");
return "http://127.0.0.1:5500/app_2/api/persons1.json";
case PersonsUrl.persons2:
devtools.log("Getting person2 url");
return "http://127.0.0.1:5500/app_2/api/persons2.json";
}
}
}
#immutable
class Person {
final String name;
final int age;
const Person({required this.name, required this.age});
Person.fromJson(Map<String, dynamic> json)
: name = json['name'] as String,
age = json['age'] as int;
}
Future<Iterable<Person>> getPersons(String url) => HttpClient()
.getUrl(Uri.parse(url))
.then((req) => req.close())
.then((resp) => resp.transform(utf8.decoder).join())
.then((str) => json.decode(str) as List<dynamic>)
.then((value) => value.map((e) => Person.fromJson(e)));
#immutable
class FetchResult {
final Iterable<Person> persons;
final bool isRetrievedFromCache;
const FetchResult(
{required this.persons, required this.isRetrievedFromCache});
#override
String toString() =>
'FetchResult (isRetrievedFromCache = $isRetrievedFromCache, persons = $persons';
}
class PersonsBloc extends Bloc<LoadAction, FetchResult?> {
final Map<PersonsUrl, Iterable<Person>> _cache = {};
PersonsBloc() : super(null) {
on<LoadPersonsAction>((event, emit) async {
final url = event.url;
devtools.log("In bloc :$url");
if (_cache.containsKey(url)) {
devtools.log("Present in cache");
final cachedPersons = _cache[url]!;
final result = FetchResult(
persons: cachedPersons,
isRetrievedFromCache: true,
);
emit(result);
} else {
devtools.log("Not present in cache");
final persons = await getPersons(url.getUrl);
if (persons.isNotEmpty) {
print("Persons is there");
} else {
print("Persons is not there");
}
_cache[url] = persons;
final result =
FetchResult(persons: persons, isRetrievedFromCache: false);
emit(result);
}
});
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({
Key? key,
}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BLoC App 2'),
),
body: Column(
children: [
Row(
children: [
TextButton(
onPressed: () {
devtools.log("Clicked to get person1");
context.read<PersonsBloc>().add(
const LoadPersonsAction(url: PersonsUrl.persons1));
},
child: const Text('Load Json #1')),
TextButton(
onPressed: () {
devtools.log("Clicked to get person2");
context.read<PersonsBloc>().add(
const LoadPersonsAction(url: PersonsUrl.persons2));
},
child: const Text('Load Json #2')),
],
),
BlocBuilder<PersonsBloc, FetchResult?>(
buildWhen: ((previous, current) {
return previous?.persons != current?.persons;
}),
builder: ((context, state) {
final persons = state?.persons;
if (persons == null) {
return const SizedBox();
}
return ListView.builder(
itemCount: persons.length,
itemBuilder: ((context, index) {
devtools.log('rendering list tiles');
final person = persons[index]!;
return ListTile(
leading: Text(person.age.toString()),
title: Text(person.name),
);
}));
}),
)
],
));
}
}
I am learning BLoC State Management from Vandad Nahavandipoor's State Management Playlist, when I click on the Load Json #1 button, it goes until the getPersons() function is called, but then doesn't fetch any data to return. Instead, it shows me some errors with no description whatsoever.
ERROR:
enter image description here
Here is the error I get after I Hot Restart:
E/flutter ( 2894): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: SocketException: Connection timed out (OS Error:
Connection timed out, errno = 110), address = 192.168.0.1, port =
51180
Although questions with such error messages exist in this site, none solves my problem.
I have a button and on clicking the button, I just need to go to a different screen. But when ever I tap on the screen, the error shows up.
I first setup a route in MaterialApp and then tried to navigate to that route on tapping the button. The full code and the error message are given below:
Code:
import 'livesession1to1.dart';
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MaterialApp(
home: CountDownTimer(),
navigatorKey: NavigationService.navigatorKey, // set property// Added by me later from prev project
// initialRoute: "/",
routes: <String, WidgetBuilder> {
'/liveSession1to1': (context) =>LiveSession1to1(),
},
)
);
}// end of main
class CountDownTimer extends StatefulWidget {
const CountDownTimer();
final String? title='';
#override
_CountDownTimerState createState() => _CountDownTimerState();
}
class _CountDownTimerState extends State<CountDownTimer> {
#override
void initState() {
super.initState();
}// end of initstate
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Live Session'),
),
body: Text('Demo Text'),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_button(title: "Go", onPressed: () =>
Navigator.of(context ,rootNavigator: true).pushNamed('/liveSession1to1', arguments: {'room_found': 123 } )
),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
);
}
Widget _button({required String title, VoidCallback? onPressed}) {
return Expanded(
child: TextButton(
child: Text(
title,
style: const TextStyle(color: Colors.white),
),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red),
),
onPressed: onPressed,
));
}
}
Error found:
The following assertion was thrown while handling a gesture:
Could not find a generator for route RouteSettings("/liveSession1to1", {room_found: 123}) in the _WidgetsAppState.
Make sure your root app widget has provided a way to generate
this route.
Generators for routes are searched for in the following order:
For the "/" route, the "home" property, if non-null, is used.
Otherwise, the "routes" table is used, if it has an entry for the route.
Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "home" and "routes".
Finally if all else fails onUnknownRoute is called.
Unfortunately, onUnknownRoute was not set.
So how to solve the problem ?
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
void main() {
locatorSetup();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final _navService = locator<NavigationHandler>();
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
onGenerateRoute: generateRoute,
navigatorKey: _navService.navigatorKey,
// I don't know what your first screen is, so I'm assuming it's a Splash Screen
home: SplashScreen());
}
}
class SplashScreen extends StatefulWidget {
const SplashScreen({Key? key}) : super(key: key);
#override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
final _navService = locator<NavigationHandler>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_navService.pushNamed(Routes.LiveSession1to1);
},
child: Text("Go to next page"),
),
));
}
}
class LiveSession1to1 extends StatefulWidget {
const LiveSession1to1({Key? key}) : super(key: key);
#override
State<LiveSession1to1> createState() => _LiveSession1to1State();
}
class _LiveSession1to1State extends State<LiveSession1to1> {
final _navService = locator<NavigationHandler>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () {
_navService.goBack();
},
child: Text("Go to previous page"),
),
));
}
}
GetIt locator = GetIt.instance;
void locatorSetup() {
locator
.registerLazySingleton<NavigationHandler>(() => NavigationHandlerImpl());
}
Route<dynamic> generateRoute(RouteSettings settings) {
switch (settings.name) {
case Routes.LiveSession1to1:
return _getPageRoute(view: LiveSession1to1(), routeName: settings.name);
default:
return MaterialPageRoute(
builder: (_) => Scaffold(
body: Center(
child: Text('No route defined for ${settings.name}'),
),
),
);
}
}
PageRoute _getPageRoute({String? routeName, Widget? view}) {
return MaterialPageRoute(
settings: RouteSettings(
name: routeName,
),
builder: (_) => view!,
);
}
class Routes {
static const String LiveSession1to1 = "liveSession1to1";
}
abstract class NavigationHandler {
///Pushes `destinationRoute` route onto the stack
Future<dynamic>? pushNamed(String destinationRoute, {dynamic arg});
///Pushes `destinationRoute` onto stack and removes stack items until
///`lastRoute` is hit
Future<dynamic>? pushNamedAndRemoveUntil(
String destinationRoute, String lastRoute,
{dynamic arg});
///Pushes `destinationRoute` onto stack with replacement
Future<dynamic>? pushReplacementNamed(String destinationRoute, {dynamic arg});
///Pushes `destinationRoute` after popping current route off stack
Future<dynamic>? popAndPushNamed(String destinationRoute, {dynamic arg});
///Pops current route off stack
void goBack();
///Pops routes on stack until `destinationRoute` is hit
void popUntil(String destinationRoute);
///Exits app
void exitApp();
late GlobalKey<NavigatorState> navigatorKey;
}
/// Handles navigation
class NavigationHandlerImpl implements NavigationHandler {
#override
late GlobalKey<NavigatorState> navigatorKey;
/// Constructs a NavigationHandler instance
NavigationHandlerImpl({GlobalKey<NavigatorState>? navigatorKey}) {
this.navigatorKey = navigatorKey ?? GlobalKey<NavigatorState>();
}
NavigatorState? get state => navigatorKey.currentState;
#override
void exitApp() {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
}
#override
void goBack() {
if (state != null) {
return state!.pop();
}
}
#override
Future? popAndPushNamed(String destinationRoute, {arg}) {
if (state != null) {
return state!.popAndPushNamed(destinationRoute, arguments: arg);
}
}
#override
void popUntil(String destinationRoute) {
if (state != null) {
return state!.popUntil(ModalRoute.withName(destinationRoute));
}
}
#override
Future? pushNamed(String destinationRoute, {arg}) {
if (state != null) {
return state!.pushNamed(destinationRoute, arguments: arg);
}
}
#override
Future? pushNamedAndRemoveUntil(String destinationRoute, String lastRoute,
{arg}) {
if (state != null) {
return state!.pushNamedAndRemoveUntil(
destinationRoute,
ModalRoute.withName(lastRoute),
arguments: arg,
);
}
}
#override
Future? pushReplacementNamed(String destinationRoute, {arg}) {
if (state != null) {
return state!.pushReplacementNamed(destinationRoute, arguments: arg);
}
}
}
I have a MultiProvider in the main with the following code:
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ReadPreferences(),
),
ChangeNotifierProvider(
create: (context) => ItemsCrud(),
),
],
child: MaterialApp(...
I am using shared preferences to save and updated the last opened list, so the following in my ReadPreferences file:
import 'package:flutter/foundation.dart'; //To use the "ChangeNotifier"
import 'package:shared_preferences/shared_preferences.dart'; //local store
class ReadPreferences extends ChangeNotifier {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
String openedList = '';
//Constructor method
ReadPreferences() {
getPreferences();
}
void getPreferences() async {
final SharedPreferences prefs = await _prefs;
openedList = prefs.getString('openedList');
}
Future<bool> updateOpenedList({String listTitle}) async {
final SharedPreferences prefs = await _prefs;
bool result = await prefs.setString('openedList', listTitle);
if (result == true) {
openedList = listTitle;
}
notifyListeners();
return result;
}
}
When I'm trying to update the opened list it updates in the shared Preferences file normally but it never listen to the new "openedList" value in my homepage screen.
The code I use in the homepage screen like the following:
child: Text(Provider.of<ReadPreferences>(context).openedList),
I checked many times by printing the new value inside the "ReadPreferences" files, but outside it, it keeps give me the old value not the updated one at all.
I tested with a modified Flutter Counter (default app), everything seams to be working fine. Note that I'm not calling setState() anywhere, so the only refresh is coming from the ReadPreferences class.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class ReadPreferences extends ChangeNotifier {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
String openedList = '';
//Constructor method
ReadPreferences() {
getPreferences();
}
void getPreferences() async {
final SharedPreferences prefs = await _prefs;
openedList = prefs.getString('openedList');
}
Future<bool> updateOpenedList({String listTitle}) async {
final SharedPreferences prefs = await _prefs;
bool result = await prefs.setString('openedList', listTitle);
if (result == true) {
openedList = listTitle;
}
notifyListeners();
return true;
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => ReadPreferences(),
)
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(Provider.of<ReadPreferences>(context).openedList)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_counter++;
Provider.of<ReadPreferences>(context, listen: false).updateOpenedList(listTitle: (_counter).toString());
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I finally found the answer, many thanks for #Andrija explanation. What I was doing wrong is to create a new instance from ReadPreferences() then using it for the update method, but the correct approach is to use Provider.of<ReadPreferences>(context, listen: false).updateOpenedList(listTitle: list.title); to use the update method.
For more explanation I'll add #Andrija comment hereafter:-
You are right, you should be using Provider.of. When you add Provider using ChangeNotifierProvider(create: (context) => ReadPreferences(), ) - new instance of ReadPreferences() is created, and it is kept in WidgetTree. This is the instance you want, and you get it by using Provider.of. In your code above, you created a new instance of ReadPreferences - and this is where you added a new value. This new instance has nothing to do with the one that Provider manages, and this new instance has nothing to do with your Widget.
I'm trying to create login with session using sharedpreferences and combine it with splashscreen but not going well, please kindly help..
Here is my code,
class SplashPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
Future cekSession() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
bool session = (preferences.getBool("session") ?? false);
if (session == true) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => MainPage()));
} else {
preferences.setBool("session", true);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Login()));
}
}
return Scaffold(
body: new SplashScreen(
seconds: 3,
// I got error around here
navigateAfterSeconds: cekSession(),
title: new Text('Welcome !'),
image: new Image.asset("assets/image.png"),
backgroundColor: Colors.white,
styleTextUnderTheLoader: new TextStyle(),
photoSize: 100.0,
loaderColor: Colors.blue),
);
}
}
error in the terminal said,
error in the device,
You can copy paste run full code below
You can check session in main
navigateAfterSeconds need widget
code snippet
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences preferences = await SharedPreferences.getInstance();
bool session = (preferences.getBool("session") ?? false);
...
navigateAfterSeconds: initScreen == "Login" ? Login() : MainPage(),
working demo
full code
import 'package:flutter/material.dart';
import 'package:splashscreen/splashscreen.dart';
import 'package:shared_preferences/shared_preferences.dart';
String initScreen;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences preferences = await SharedPreferences.getInstance();
bool session = (preferences.getBool("session") ?? false);
if (session == true) {
initScreen = "MainPage";
} else {
preferences.setBool("session", true);
initScreen = "Login";
}
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SplashScreen(
seconds: 3,
navigateAfterSeconds: initScreen == "Login" ? Login() : MainPage(),
title: Text(
'Welcome In SplashScreen',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20.0),
),
image: Image.network('https://i.imgur.com/TyCSG9A.png'),
backgroundColor: Colors.white,
styleTextUnderTheLoader: TextStyle(),
photoSize: 100.0,
onClick: () => print("Flutter Egypt"),
loaderColor: Colors.red),
);
}
}
class Login extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Login");
}
}
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Main Page");
}
}
Because navigateAfterSeconds does not receive Future, try to pass a widget instead. Take a StateFul Widget. You need to declare a bool isLoggiedIn need to evaluate your checkSession result in initState before moving to another screen.
bool isLoggiedIn;
#override
void initState() {
super.initState();
checkSession();
}
void checkSession(){
setState{(
isLoggiedIn= your value from sharedpref
)}
}
then on behalf of isLoggiedIn value you need to pass widget like this,
if (session == true) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => MainPage()));
} else {
preferences.setBool("session", true);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Login()));
}