how to use a shared preference from a change notifier - flutter

I'm trying to get my head around the shared preference package from a change notifier. here is my code:
Future<void> main() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => GlobalSettings1(prefs: prefs)),
],
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: {
'/': (context) => Page01(),
});
}
}
class Page01 extends StatefulWidget {
const Page01({Key? key}) : super(key: key);
#override
State<Page01> createState() => _Page01State();
}
class _Page01State extends State<Page01> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 30,),
InkWell(
onTap: () {
context.read<GlobalSettings1>().ToggleSwitch();
},
child: Container(
height: 30,
width: 80,
color: context.watch<GlobalSettings1>().toggleSwitch01
? Colors.green
: Colors.red,
),
),
],
),
);
}
}
and here is my change notifier:
class GlobalSettings1 with ChangeNotifier {
bool _toggleSwitch01 = true;
final SharedPreferences prefs;
GlobalSettings1({required this.prefs});
bool get toggleSwitch01 => _toggleSwitch01;
void ToggleSwitch() {
_toggleSwitch01 = !_toggleSwitch01;
_setPrefItems();
notifyListeners();
}
void _setPrefItems() {
prefs.setBool('toggleSwitch01', _toggleSwitch01);
notifyListeners();
}
void _getPrefItems() {
_toggleSwitch01 = prefs.getBool('toggleSwitch01') ?? true;
notifyListeners();
}
bool getToggleSwitch01() {
_getPrefItems();
return _toggleSwitch01;
}
}
How do i use setprefitems and getprefitems in my code to make it so the toggleswitch01 bools state is saved for when the app is closed and then statrted again?
thanks so much

Related

How to Hide bottom navigation bar using provider flutter

I am working on a social media app which contains a home screen with bottom navbar and 5 pages .
Although i am able to change the bool value to show or hide navbar under provider but the changes are not reflecting in widget .
main.dart -
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) =>
ScrollProvider(),
),
],
child : MyApp()
My provider class -
class ScrollProvider with ChangeNotifier{
bool isVisible = true;
void show() {
isVisible = true;
print("in Provider $isVisible");
notifyListeners();
}
void hide() {
isVisible = false;
print("in Provider $isVisible");
notifyListeners();
}
}
Page which has the value scrollcontroller
ScrollController _scrollController =
ScrollController();
_scrollController.addListener(() {
final direction =
_scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
ScrollProvider().show();
}
if (direction == ScrollDirection.reverse) {
ScrollProvider().hide();
}
});
And my homescreen which which has body and navbar contains this code
‘’’
bottomNavigationBar: Consumer<BottomBarVisibilityProvider>(
builder: (context, bottomBarVisibilityProvider, child) =>
AnimatedContainer(
duration: const Duration(milliseconds: 200),
child: bottomBarVisibilityProvider.isVisible
? Wrap(
children: const [BottomBar()],
)
: Wrap(),
),
),
I have initialised the provider inside main.dart
Can someone tell me why its not working .. and what should i do
here is the full code of homepage
class _HomeState extends State<Home> {
List<Widget> _pages ;
#override
void initState() {
super.initState();
_pages = [
MemeTabView(scrollController: scrollToHide),
TempTab(),
null,
NotificationScreen(),
ProfileScreen(uid: FirebaseAuth.instance.currentUser.uid,showAppBar: false),
];
}
#override
Widget build(BuildContext context) {
if (widget.selectedIndex == null) {
widget.selectedIndex = 0;
}
return
Scaffold(
appBar: AppBar(
title: brandName(),
),
extendBody: true,
bottomNavigationBar:
Consumer<ScrollProvider>(
builder: (_,scrollProvider,__){
return Container(
child: scrollProvider.isVisible == true
?
bottomNav()
: Container(),
);
},
),
drawer: MyDrawer(),
body:PageView(
controller: _pageController,
physics: NeverScrollableScrollPhysics(),
children: _pages,
),
);
}
You need to read provider like
context.read<BottomBarVisibilityProvider>();
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => BottomBarVisibilityProvider()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.\
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: const SCCR(),
);
}
}
class SCCR extends StatefulWidget {
const SCCR({Key? key}) : super(key: key);
#override
State<SCCR> createState() => _SCCRState();
}
class _SCCRState extends State<SCCR> {
//just for test purpose
late final bottomBarVisibilityProvider =
context.read<BottomBarVisibilityProvider>();
late final ScrollController scrollController = ScrollController()
..addListener(() {
final direction = scrollController.position.userScrollDirection;
if (direction == ScrollDirection.forward) {
if (!bottomBarVisibilityProvider.isVisible) {
bottomBarVisibilityProvider.show();
}
} else if (direction == ScrollDirection.reverse) {
if (bottomBarVisibilityProvider.isVisible) {
bottomBarVisibilityProvider.hide();
}
}
});
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
controller: scrollController,
child: Container(
height: 3333,
),
),
bottomNavigationBar: Consumer<BottomBarVisibilityProvider>(
builder: (context, bottomBarVisibilityProvider, child) =>
AnimatedContainer(
duration: const Duration(milliseconds: 200),
child: bottomBarVisibilityProvider.isVisible
? Wrap(
children: [
Container(
height: 100,
color: Colors.red,
width: 100,
)
],
)
: null,
),
),
);
}
}
I will suggest to follow documentation
This is how I use my providers:
I have 3 different providers here, MyThemeClass, NewMsgListener and FirebaseProvider. All of them extend ChangeNotifierProvider. And this is my main:
import 'package:my_app/my_firebase.dart';
import 'package:my_app/global.dart';
import 'package:provider/provider.dart';
import 'package:navigation_history_observer/navigation_history_observer.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:my_app/firebase_options.dart';
import 'package:my_app/screens/welcome_screen.dart';
import 'package:my_app/new_msg_listener.dart';
import 'package:my_app/provider_updates.dart';
import 'package:my_app/theme.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
MyFirebase.myFutureFirebaseApp =
Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
final NewMsgListener newMsgListener = NewMsgListener();
final MyThemeClass myThemeClass = MyThemeClass();
await myThemeClass.getMyTheme();
print('getMyTheme() is ready');
await MyFirebase.myFutureFirebaseApp;
runApp(MyApp(myThemeClass, newMsgListener));
}
class MyApp extends StatelessWidget {
const MyApp(this.myThemeClass, this.newMsgListener, {Key? key})
: super(key: key);
final MyThemeClass myThemeClass;
final NewMsgListener newMsgListener;
#override
Widget build(BuildContext context) {
print('Building $runtimeType');
FirebaseProvider firebaseProvider = FirebaseProvider();
firebaseProvider.initialize(GlobalVariable.navState);
return MultiProvider(
providers: [
ChangeNotifierProvider<FirebaseProvider>(
create: (context) => firebaseProvider),
ChangeNotifierProvider(create: (context) => myThemeClass),
ChangeNotifierProvider(create: (context) => newMsgListener),
],
builder: (context, child) {
return MaterialApp(
theme: Provider.of<MyThemeClass>(context).currentTheme,
home: WelcomeScreen(myThemeClass),
debugShowCheckedModeBanner: false,
navigatorObservers: [NavigationHistoryObserver()],
navigatorKey: GlobalVariable.navState,
);
});
}
}
Not that it's important, but my my_firebase.dart file looks like this (just so you don't need to wonder!):
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:my_app/firebase_labels.dart';
class MyFirebase {
static Future<FirebaseApp>? myFutureFirebaseApp; //Initialized from main()
static FirebaseFirestore storeObject = FirebaseFirestore.instance;
static auth.FirebaseAuth authObject = auth.FirebaseAuth.instance;
}
Does that provider management work for you?

Animated moveable list in flutter?

any tips or help how can I make this on tap moveable list in flutter?
https://files.fm/f/txdn29dg3
The provided component is exactly what CupertinoPicker could offer you.
Also, as suggested in the documentation, you should combine the CupertinoPicker with showCupertinoModalPopup to display the picker modally at the bottom of the screen.
This is how the code could look like:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: PickerPage(),
),
),
);
}
}
class PickerPage extends StatefulWidget {
const PickerPage();
#override
_PickerPageState createState() => _PickerPageState();
}
class _PickerPageState extends State<PickerPage> {
final _items = [
'Flat Rate',
'Hourly',
'Request for Price',
];
int _selectedItem = 0;
void _onSelectedItemChanged(int value) => setState(
() => _selectedItem = value,
);
void _showPicker() {
showCupertinoModalPopup(
context: context,
builder: (_) => PickerExample(
items: _items,
selectedItem: _selectedItem,
onSelectedItemChanged: _onSelectedItemChanged,
),
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_items[_selectedItem]),
const SizedBox(height: 10.0),
ElevatedButton(
child: const Text('Show picker'),
onPressed: _showPicker,
),
],
);
}
}
class PickerExample extends StatefulWidget {
final List<String> items;
final int selectedItem;
final ValueSetter<int> onSelectedItemChanged;
const PickerExample({
required this.items,
required this.selectedItem,
required this.onSelectedItemChanged,
});
#override
_PickerExampleState createState() => _PickerExampleState();
}
class _PickerExampleState extends State<PickerExample> {
late final FixedExtentScrollController _controller;
#override
void initState() {
super.initState();
_controller = FixedExtentScrollController(initialItem: widget.selectedItem);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
child: CupertinoPicker(
scrollController: _controller,
backgroundColor: Colors.white,
itemExtent: 30.0,
children: [
for (final item in widget.items) Center(child: Text(item)),
],
onSelectedItemChanged: widget.onSelectedItemChanged,
),
);
}
}
You could also find an interactive example in this DartPad.

Rid of elevation of Nested Flutter Navigator 2.0

Try to use Navigation 2.0 for a web project. I added a nested navigator, but I do not like the elevation that comes with the nested Navigator.
Ugly elevation
import 'package:flutter/material.dart';
import 'package:move_to_background/move_to_background.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Router(
routerDelegate: AuthenticationRouterDelegate(),
backButtonDispatcher: RootBackButtonDispatcher(),
),
);
}
}
class AuthenticationRouterDelegate extends RouterDelegate with ChangeNotifier {
bool isAuthenticated = false;
final GlobalKey<NavigatorState> navigatorKey;
AuthenticationRouterDelegate() : navigatorKey = GlobalKey<NavigatorState>();
#override
Future<bool> popRoute() async {
print('popRoute AuthenticationRouterDelegate');
MoveToBackground.moveTaskToBack();
return true;
}
#override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
observers: [HeroController()],
pages: [
MaterialPage(
key: ValueKey('MyHomePage'),
child: MyAuthenticationWidget(
onPressed: () {
isAuthenticated = true;
notifyListeners();
},
),
),
if (isAuthenticated)
MaterialPage(
key: ValueKey('NestedNavigatorPage'),
child: NestedRouterWidget(),
),
],
onPopPage: (route, result) {
print('onPopPage AuthenticationRouterDelegate');
if (!route.didPop(result)) return false;
isAuthenticated = false;
notifyListeners();
return true;
},
);
}
// We don't use named navigation so we don't use this
#override
Future<void> setNewRoutePath(configuration) async => null;
}
class MyAuthenticationWidget extends StatelessWidget {
final VoidCallback onPressed;
MyAuthenticationWidget({required this.onPressed}) : super();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
flex: 1,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Side block'),
],
),
),
Expanded(
flex: 2,
child: Center(
child: NestedRouterWidget(),
),
)
],
),
);
}
}
class NestedRouterWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
final childBackButtonDispatcher =
ChildBackButtonDispatcher(Router.of(context).backButtonDispatcher!);
childBackButtonDispatcher.takePriority();
return Router(
routerDelegate: NestedRouterDelegate(),
backButtonDispatcher: childBackButtonDispatcher,
);
}
}
final GlobalKey<NavigatorState> _nestedNavigatorKey =
GlobalKey<NavigatorState>();
class NestedRouterDelegate extends RouterDelegate with ChangeNotifier {
int selectedIndex = 0;
#override
Future<bool> popRoute() async {
print('popRoute NestedRouterDelegate');
return false;
}
#override
Widget build(BuildContext context) {
return Navigator(
key: _nestedNavigatorKey,
observers: [HeroController()],
pages: [
if (selectedIndex == 0)
MaterialPage(
key: ValueKey('ProfilePage'),
child: ProfileWidget(
onPressed: () {},
),
),
if (selectedIndex == 1)
MaterialPage(
key: ValueKey('NestedNavigatorPage'),
child: SettingWidget(),
),
],
onPopPage: (route, result) {
print('onPopPage NestedRouterDelegate');
return false;
},
);
}
// We don't use named navigation so we don't use this
#override
Future<void> setNewRoutePath(configuration) async => null;
}
class ProfileWidget extends StatelessWidget {
final VoidCallback onPressed;
ProfileWidget({required this.onPressed}) : super();
#override
Widget build(BuildContext context) {
// omit
}
}
class SettingWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
// omit
}
}
Full source code on GitHub here https://github.com/AndrewPiterov/flutter_web_nested_navigator/blob/main/lib/main.dart
How to remove this elevation? Thanks!
At the end, figured out the solution is to set fullscreenDialog to true
MaterialPage(
key: ValueKey('ProfilePage'),
fullscreenDialog: true,
child: ProfileWidget(
onPressed: () {},
),
),

TimeServiceProvider can't be assigned to the list type SingleChildWidget

The following Flutter code is for a Stopwatch that persistently can run in the background. It works great when I use only one provider:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(
TimerServiceProvider(
service: TimerService(),
child: MyApp(),
),
);
}
class TimerService extends ChangeNotifier {
Stopwatch _watch;
Timer _timer;
Duration get currentDuration => _currentDuration;
Duration _currentDuration = Duration.zero;
bool get isRunning => _timer != null;
TimerService() {
_watch = Stopwatch();
}
void _onTick(Timer timer) {
_currentDuration = _watch.elapsed;
// notify all listening widgets
notifyListeners();
}
void start() {
if (_timer != null) return;
_timer = Timer.periodic(Duration(seconds: 1), _onTick);
_watch.start();
notifyListeners();
}
void stop() {
_timer?.cancel();
_timer = null;
_watch.stop();
_currentDuration = _watch.elapsed;
notifyListeners();
}
void reset() {
stop();
_watch.reset();
_currentDuration = Duration.zero;
notifyListeners();
}
static TimerService of(BuildContext context) {
var provider =
context.dependOnInheritedWidgetOfExactType<TimerServiceProvider>();
return provider.service;
}
}
class TimerServiceProvider extends InheritedWidget {
const TimerServiceProvider({Key key, this.service, Widget child})
: super(key: key, child: child);
final TimerService service;
#override
bool updateShouldNotify(TimerServiceProvider old) => service != old.service;
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Service Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
var timerService = TimerService.of(context);
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedBuilder(
animation: timerService, // listen to ChangeNotifier
builder: (context, child) {
// this part is rebuilt whenever notifyListeners() is called
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Elapsed: ${timerService.currentDuration}'),
RaisedButton(
onPressed: !timerService.isRunning
? timerService.start
: timerService.stop,
child: Text(!timerService.isRunning ? 'Start' : 'Stop'),
),
RaisedButton(
onPressed: timerService.reset,
child: Text('Reset'),
)
],
);
},
),
),
);
}
}
However, I need to use more than one provider in my app and when I wrap this provider in a MultiProvider I get an error saying that error: The element type 'TimerServiceProvider' can't be assigned to the list type 'SingleChildWidget'. (list_element_type_not_assignable
The code I used to wrap the provider in a MultiProvider looks as follows:
void main() {
runApp(
MultiProvider(
providers: [
TimerServiceProvider(service: TimerService(),
),
],
child: MyApp(),)
);
}
Does anyone know what I'm doing wrong?
You can copy paste run full code below
You can use ChangeNotifierProvider and Provider.of<TimerService>
code snippet
void main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => TimerService()),
],
child: MyApp(),
));
}
...
#override
Widget build(BuildContext context) {
var timerService = Provider.of<TimerService>(context, listen: false);
working demo
full code
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => TimerService()),
],
child: MyApp(),
));
}
class TimerService extends ChangeNotifier {
Stopwatch _watch;
Timer _timer;
Duration get currentDuration => _currentDuration;
Duration _currentDuration = Duration.zero;
bool get isRunning => _timer != null;
TimerService() {
_watch = Stopwatch();
}
void _onTick(Timer timer) {
_currentDuration = _watch.elapsed;
// notify all listening widgets
notifyListeners();
}
void start() {
if (_timer != null) return;
_timer = Timer.periodic(Duration(seconds: 1), _onTick);
_watch.start();
notifyListeners();
}
void stop() {
_timer?.cancel();
_timer = null;
_watch.stop();
_currentDuration = _watch.elapsed;
notifyListeners();
}
void reset() {
stop();
_watch.reset();
_currentDuration = Duration.zero;
notifyListeners();
}
/*static TimerService of(BuildContext context) {
var provider =
context.dependOnInheritedWidgetOfExactType<TimerServiceProvider>();
return provider.service;
}*/
}
class TimerServiceProvider extends InheritedWidget {
const TimerServiceProvider({Key key, this.service, Widget child})
: super(key: key, child: child);
final TimerService service;
#override
bool updateShouldNotify(TimerServiceProvider old) => service != old.service;
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Service Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
var timerService = Provider.of<TimerService>(context, listen: false);
return Scaffold(
appBar: AppBar(),
body: Center(
child: AnimatedBuilder(
animation: timerService, // listen to ChangeNotifier
builder: (context, child) {
// this part is rebuilt whenever notifyListeners() is called
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Elapsed: ${timerService.currentDuration}'),
RaisedButton(
onPressed: !timerService.isRunning
? timerService.start
: timerService.stop,
child: Text(!timerService.isRunning ? 'Start' : 'Stop'),
),
RaisedButton(
onPressed: timerService.reset,
child: Text('Reset'),
)
],
);
},
),
),
);
}
}

What is the proper way of using SharedPreferences with Provider in Flutter?

I am newbie to state management using provider in flutter.
I've created a model named as Counter:
import 'package:flutter/foundation.dart';
class Counter with ChangeNotifier {
int value = 0;
void increment() {
value++;
notifyListeners();
}
void decrement() {
value--;
notifyListeners();
}
}
Now when value changes I can save it locally using SharedPreferences in order to start from that value next time.
But, I do not know what would be a proper way of loading data from local and set value in Counter class.
Should I load saved data in main.dart file when app is initalized and then setValue to that data?
Or are there any solutions, for example, loading data directly in my Counter class?
create a SharedPreferencesProvider
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesProvider {
final Future<SharedPreferences> sharedPreferences;
SharedPreferencesProvider(this.sharedPreferences);
Stream<SharedPreferences> get prefsState => sharedPreferences.asStream();
}
then create a Provider and with a StreamProvider as shown below
return MultiProvider(
providers: [
Provider<SharedPreferencesProvider>(create: (_) => SharedPreferencesProvider(SharedPreferences.getInstance())),
StreamProvider(create: (context) => context.read<SharedPreferencesProvider>().prefsState, initialData: null)
then consume the state within a Widget build with a context.watch
#override
Widget build(BuildContext context) {
sharedPrefs = context.watch<SharedPreferences>();
Try to use the future builder and then set it to the provider and be able to use SharedPreferences everywhere in the app:
#override
Widget build(BuildContext context) {
return FutureBuilder<SharedPreferences>(
future: SharedPreferences.getInstance(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data != null) {
return MultiProvider(providers: [
Provider<SharedPreferences>(
create: (context) => snapshot.data!,
),
],
);
}
},
);
}
And you can use context.read() everywhere.
The question is leaning toward opinion. I'm also new to flutter -- the below may not be the best way, but it does work, so maybe it will help someone.
If it's the top level app, you can initialize the counter before actually using it, displaying a loading page during the load time (imperceptible in this case). You must include the first runApp however, otherwise shared_preferences will not be able to correctly access the file containing these preferences on the device.
A similar thing can be done with with FutureBuilder, but you must await a delay prior to attempting to read from shared_preferences.
(I don't think the loading page or delay are necessary if you aren't using the widget as your top level widget, which would probably be better anyway. In that case, probably FutureBuilder would be the correct solution. (?))
To note:
I added an async "constructor" to the Counter class that initializes from the shared_preferences.
I access the Counter via provider library in _MyHomePageState.build with context.watch<Counter>(), which causes this to rebuild on changes (without requiring calls to setState.
I've added async Counter._updatePreferences which is called in Counter.increment and Counter.decrement, which saves the current value of the Counter to the shared_preferences.
Imports and main for first method
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> main() async {
// run "loading" app while awaiting counter, then run app again
runApp(
const MaterialApp(
home: Center(
child: Text('Loading'),
),
)
);
final Counter counter = await Counter.fromPreferences();
runApp(
ChangeNotifierProvider<Counter>.value(
value: counter,
child: const MyApp(),
)
);
}
Imports and main (with FutureBuilder)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
// Get counter in future builder
runApp(
FutureBuilder<Counter>(
future: Counter.fromPreferences(),
builder: (BuildContext context, AsyncSnapshot<Counter> snapshot) {
Widget returnWidget = const MaterialApp(
home: Center(
child: Text('Loading'),
),
);
if (snapshot.connectionState == ConnectionState.waiting) {
} else if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
print(snapshot.error);
} else if (snapshot.hasData) {
final Counter counter = snapshot.data!;
returnWidget = ChangeNotifierProvider<Counter>.value(
value: counter,
child: const MyApp(),
);
} else {
print('No data');
}
} else if (snapshot.connectionState == ConnectionState.none) {
print('null future');
} else {
print(snapshot.connectionState);
}
return returnWidget;
},
),
);
}
MyApp and MyHomePage
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Counter App',
home: MyHomePage(title: 'Counter App Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final Counter counter = context.watch<Counter>();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${counter.value}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <FloatingActionButton>[
FloatingActionButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
FloatingActionButton(
onPressed: counter.decrement,
child: const Icon(Icons.remove),
),
],
),
);
}
}
Counter Class (ChangeNotifier)
class Counter extends ChangeNotifier {
int value = 0;
static Future<Counter> fromPreferences() async {
final Counter counter = Counter();
// Must be included if using the FutureBuilder
// await Future<void>.delayed(Duration.zero, () {});
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int value = prefs.getInt('counterValue') ?? 0;
counter.value = value;
return counter;
}
Future<void> _updatePreferences() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('counterValue', value);
}
void increment() {
value++;
notifyListeners();
_updatePreferences();
}
void decrement() {
value--;
notifyListeners();
_updatePreferences();
}
}
Complete Example
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> main() async {
// run "loading" app while awaiting counter, then run app again
runApp(
const MaterialApp(
home: Center(
child: Text('Loading'),
),
)
);
final Counter counter = await Counter.fromPreferences();
runApp(
ChangeNotifierProvider<Counter>.value(
value: counter,
child: const MyApp(),
)
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Counter App',
home: MyHomePage(title: 'Counter App Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final Counter counter = context.watch<Counter>();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'${counter.value}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <FloatingActionButton>[
FloatingActionButton(
onPressed: counter.increment,
child: const Icon(Icons.add),
),
FloatingActionButton(
onPressed: counter.decrement,
child: const Icon(Icons.remove),
),
],
),
);
}
}
class Counter extends ChangeNotifier {
int value = 0;
static Future<Counter> fromPreferences() async {
final Counter counter = Counter();
// Must be included if using the FutureBuilder
// await Future<void>.delayed(Duration.zero, () {});
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int value = prefs.getInt('counterValue') ?? 0;
counter.value = value;
return counter;
}
Future<void> _updatePreferences() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('counterValue', value);
}
void increment() {
value++;
notifyListeners();
_updatePreferences();
}
void decrement() {
value--;
notifyListeners();
_updatePreferences();
}
}
Use the shared_preferences plugin
enter link description here
import 'dart:async';
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: 'SharedPreferences Demo',
home: SharedPreferencesDemo(),
);
}
}
class SharedPreferencesDemo extends StatefulWidget {
SharedPreferencesDemo({Key key}) : super(key: key);
#override
SharedPreferencesDemoState createState() => SharedPreferencesDemoState();
}
class SharedPreferencesDemoState extends State<SharedPreferencesDemo> {
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<int> _counter;
Future<void> _incrementCounter() async {
final SharedPreferences prefs = await _prefs;
final int counter = (prefs.getInt('counter') ?? 0) + 1;
setState(() {
_counter = prefs.setInt("counter", counter).then((bool success) {
return counter;
});
});
}
#override
void initState() {
super.initState();
_counter = _prefs.then((SharedPreferences prefs) {
return (prefs.getInt('counter') ?? 0);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("SharedPreferences Demo"),
),
body: Center(
child: FutureBuilder<int>(
future: _counter,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const CircularProgressIndicator();
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return Text(
'Button tapped ${snapshot.data} time${snapshot.data == 1 ? '' : 's'}.\n\n'
'This should persist across restarts.',
);
}
}
})),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Reference site : https://pub.dev/packages/shared_preferences#-example-tab-