my widget didn't rebuild when remove item from list unless make hot-reload for project - flutter

my project about Build music App using flutter-provider and packages name (on_audio_query) so when I get all songs from storage and want to modified them, like remove or add item the widget didn't rebuild automatically the update happened in console only unless press hot-reload to all project and the list has update and can review my code below.
main.dart
import 'package:flutter/material.dart';
import 'package:music_app_v4/test_folder/home_screen.dart';
import 'package:music_app_v4/test_folder/home_model.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MultiProvider(providers: [
ChangeNotifierProvider<HomeModel>(create: (context) => HomeModel()),
],
child: const MyApp(),));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter List Songs'),
);
}
}
HomeScreen.dart
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:music_app_v4/test_folder/home_model.dart';
import 'package:on_audio_query/on_audio_query.dart';
import 'package:provider/provider.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final OnAudioQuery _onAudioQuery = OnAudioQuery();
#override
void initState() {
requestStoragePermission();
super.initState();
}
void requestStoragePermission() async {
try {
if (!kIsWeb) {
bool status = await _onAudioQuery.permissionsStatus();
if (!status) {
await _onAudioQuery.permissionsRequest().then((value) => HomeModel());
}
setState(() {});
}
} catch (e) {
const SocketException.closed();
rethrow;
}
}
#override
Widget build(BuildContext context) {
//var notify = Provider.of<HomeModel>(context,listen: false);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Consumer<HomeModel>(builder: (context,notify,child){
return FutureBuilder<List<SongModel>>(
future: _onAudioQuery.querySongs(ignoreCase: false),
builder: (context,AsyncSnapshot item) {
if (item.data == null) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (item.data!.isEmpty) {
return const Center(
child: Text('No found Data'),
);
}
List<SongModel> songs = item.data!;
return ListView.builder(
itemCount: songs.length,
itemBuilder: (context, index) {
return Row(
children: [
Expanded(
child: ListTile(
title: Text(songs[index].title),
),
),
GestureDetector(
onTap: () {
// setState(() {
// _audioEdit.deleteAudio(songs[index].data);
// });
notify.removeAudio(songs[index].data);
print('button pressed');
//_audioEdit.deleteAudio(songs[index].data);
},
child: Icon(Icons.remove_circle))
],
);
},
);
},
);
},),
);
}
}
HomeModel.dart
import 'package:flutter/material.dart';
import 'package:on_audio_edit/on_audio_edit.dart';
import 'package:on_audio_query/on_audio_query.dart';
class HomeModel extends ChangeNotifier{
final OnAudioEdit _audioEdit = OnAudioEdit();
final OnAudioQuery onAudioQuery = OnAudioQuery();
List<SongModel>? songs;
void removeAudio(dynamic data){
_audioEdit.deleteAudio(data);
notifyListeners();
}
}

Related

How to show updated list in shared preferences on UI - Flutter

I am making an app in a flutter in which I can select the contacts from phone book and saving them in shared preferences. No problem in data saving and retrieving but i m struggling with showing the updated list on my UI. It is showing the contacts list but every time I click on Load button it duplicates the list and showing 2 lists , 1 previous and other updated .
how can i show just updated list on UI ?
here is my code:
import 'package:contacts_test/select_contacts.dart';
import 'package:contacts_test/shared_pref.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'contact_model.dart';
void main() {
runApp(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 const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SharedPref sharedPref = SharedPref();
ContactModel modelLoad = ContactModel(displayName: 'saniya' , phoneNumber: '324235 ');
List _list = [];
#override
initState() {
super.initState();
// Add listeners to this clas
// loadSharedPrefs();
}
loadSharedPrefs() async {
try {
print('in load shared pref-- getting keys ');
final prefs = await SharedPreferences.getInstance();
final keys = prefs.getKeys();
print('now load shared pref ');
for (String key in keys) {
ContactModel user = ContactModel.fromJson(await sharedPref.read(key));
_list.add(user);
}
print('done load shared pref ');
}
catch (Excepetion) {
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("contacts "),
),
body: Builder(
builder: (context) {
return Column(children: [
RaisedButton(
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Plugin1()));
},
child: const Text('fluttercontactpicker - plugin1'),
),
RaisedButton(
onPressed: () async {
await loadSharedPrefs();
},
child: Text('Load', style: TextStyle(fontSize: 20)),
),
Expanded(
child: _list.isNotEmpty ?
ListView.builder(
shrinkWrap: true,
itemCount: _list.length,
itemBuilder: (context, position) {
return ListTile(
leading: Icon(Icons.contacts),
title: Text(
_list[position].displayName.toString()
),
trailing: Icon(Icons.delete));
},
) : Center(child: Text('No list items to show')),
),
]);
}
),
);
}
}
Your loadSharedPrefs(); function adds each contact to the list you show. Every time you press the button, the same elements are added again to the list. There are multiple ways to avoid that. You can: empty the list before filling it, you can write a for loop to loop over the length of the incoming contacts and for each to add it to the list by always starting from index 0. In case you use some kind of replacement or removing method, make sure you call setState(()=> { });
Base on the answer, here is a possible solution:
import 'package:contacts_test/select_contacts.dart';
import 'package:contacts_test/shared_pref.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'contact_model.dart';
void main() {
runApp(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 const MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SharedPref sharedPref = SharedPref();
ContactModel modelLoad = ContactModel(displayName: 'saniya' , phoneNumber: '324235 ');
List _list = [];
#override
initState() {
super.initState();
// Add listeners to this clas
// loadSharedPrefs();
}
loadSharedPrefs() async {
try {
print('in load shared pref-- getting keys ');
final prefs = await SharedPreferences.getInstance();
final keys = prefs.getKeys();
print('now load shared pref ');
var newList = [];
for (String key in keys) {
ContactModel user = ContactModel.fromJson(await sharedPref.read(key));
newList.add(user);
}
setState(()=> { _list = newList; });
print('done load shared pref ');
}
catch (Excepetion) {
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("contacts "),
),
body: Builder(
builder: (context) {
return Column(children: [
RaisedButton(
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => Plugin1()));
},
child: const Text('fluttercontactpicker - plugin1'),
),
RaisedButton(
onPressed: () async {
await loadSharedPrefs();
},
child: Text('Load', style: TextStyle(fontSize: 20)),
),
Expanded(
child: _list.isNotEmpty ?
ListView.builder(
shrinkWrap: true,
itemCount: _list.length,
itemBuilder: (context, position) {
return ListTile(
leading: Icon(Icons.contacts),
title: Text(
_list[position].displayName.toString()
),
trailing: Icon(Icons.delete));
},
) : Center(child: Text('No list items to show')),
),
]);
}
),
);
}
}

Bloc State is yielded but UI is not updating

I'm having an issue using bloc. The bloc state is updating but it seems to be no effect on UI. I have reproduced the issue using an example app. The issue is removing an item from the list page does not change is color back to deselected state. Actually what happens is When you select an item on home page the cart button appears and on no items selected state it disappears. So when you selected an item on homepage the cart button appears. When you click on the button you're navigated to another page i.e. ListPage where you can see the list of all selected items and a remove button corresponding to every item. When you remove an item it gets removed from ListPage but still it's color is Red i.e. selected on HomePage. I have to click again to change the color although it was deselected i.e. (item.active = false) on removing it from the ListPage. Just the color stays the same, it's not getting updated. The code files are given below:
item_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/item_model.dart';
import 'package:demo/repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
part 'items_event.dart';
part 'items_state.dart';
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
final ItemRepository itemRepository;
final CartBloc cartBloc;
ItemsBloc({
#required this.itemRepository,
#required this.cartBloc,
}) : super(ItemsLoading());
#override
Stream<ItemsState> mapEventToState(
ItemsEvent event,
) async* {
if (event is GetItems) {
yield* _mapGetItemsToState(event);
} else if (event is UpdateItem) {
yield* _mapUpdateItemToState(event);
}
}
Stream<ItemsState> _mapGetItemsToState(GetItems event) async* {
yield ItemsLoading();
try {
final _items = await itemRepository.loadItems();
yield ItemsLoaded(items: _items);
print('Yielded');
} catch (_) {
yield ItemsFailure();
}
}
Stream<ItemsState> _mapUpdateItemToState(UpdateItem event) async* {
try {
_updateRepositoryItems(event.item);
if (event.item.active) {
cartBloc.add(ShowItems());
} else {
cartBloc.add(RemoveItem(itemId: event.item.id));
}
yield* _mapGetItemsToState(GetItems());
} catch (_) {
yield ItemsFailure();
}
}
Item _updateRepositoryItems(Item item) => itemRepository.setItem = item;
}
cart_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:demo/item_data_model.dart';
import 'package:demo/repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
part 'cart_event.dart';
part 'cart_state.dart';
class CartBloc extends Bloc<CartEvent, CartState> {
final ItemRepository itemRepository;
CartBloc({#required this.itemRepository}) : super(CartLoading());
#override
Stream<CartState> mapEventToState(
CartEvent event,
) async* {
if (event is ShowItems) {
yield* _mapShowItemsToState(event);
} else if (event is RemoveItem) {
yield* _mapRemoveItemToState(event);
}
}
Stream<CartState> _mapShowItemsToState(ShowItems event) async* {
try {
final _selectedItems = await itemRepository.getSelectedItems;
if (_selectedItems.isNotEmpty) {
final data = ItemData(items: _selectedItems);
yield CartLoaded(itemData: data);
} else {
yield CartLoading();
}
} catch (_) {
yield CartFailure();
}
}
Stream<CartState> _mapRemoveItemToState(RemoveItem event) async* {
try {
final _selectedSounds = await itemRepository.getSelectedItems;
if (_selectedSounds.isEmpty) {
yield CartLoading();
} else {
yield* _mapShowItemsToState(ShowItems());
}
} catch (_) {
yield CartFailure();
}
}
}
homepage.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/item_tile.dart';
import 'package:demo/list_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Home Page"),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: BlocBuilder<ItemsBloc, ItemsState>(
// ignore: missing_return
builder: (context, state) {
if (state is ItemsLoading) {
return Center(
child: CircularProgressIndicator(),
);
} else if (state is ItemsLoaded) {
print('Items Loaded');
return GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
childAspectRatio: 0.6,
children: [
for (var item in state.items) ItemTile(item: item),
],
);
} else if (state is ItemsFailure) {
return Center(
child: Text(
'Unexpected Failure, Cannot Load Items',
style: Theme.of(context).textTheme.headline6,
),
);
}
},
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: CartButton(),
),
],
),
),
);
}
}
class CartButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<CartBloc, CartState>(
// ignore: missing_return
builder: (context, state) {
if (state is CartLoading) {
return SizedBox.shrink();
} else if (state is CartLoaded) {
print('Cart Loaded');
return TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ListPage(
items: state.itemData.items,
);
}),
);
},
child: Text('Go To Cart'),
);
} else if (state is CartFailure) {
return Center(
child: Text(
'Unexpected Failure, Cannot Load Cart',
style: Theme.of(context).textTheme.headline6,
),
);
}
},
);
}
}
item_tile.dart
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:demo/item_model.dart';
import 'package:flutter/material.dart';
class ItemTile extends StatefulWidget {
final Item item;
const ItemTile({Key key, this.item}) : super(key: key);
#override
_ItemTileState createState() => _ItemTileState();
}
class _ItemTileState extends State<ItemTile> {
bool active;
#override
void initState() {
super.initState();
active = widget.item.active;
}
void _onItemTap() {
setState(() {
active = !active;
});
getIt<ItemsBloc>()
.add(UpdateItem(item: widget.item.copyWith(active: active)));
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.item.title),
tileColor: active ? Colors.redAccent : Colors.blueAccent,
onTap: () {
_onItemTap();
},
);
}
}
item_page.dart
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:flutter/material.dart';
import 'item_model.dart';
class ListPage extends StatefulWidget {
final List<Item> items;
const ListPage({Key key, this.items}) : super(key: key);
#override
_PlayerScreenState createState() => _PlayerScreenState();
}
class _PlayerScreenState extends State<ListPage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
'Cart List Page',
style: Theme.of(context).textTheme.headline6,
),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.separated(
itemCount: widget.items.length,
separatorBuilder: (BuildContext context, int index) =>
Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
trailing: IconButton(
icon: Icon(
Icons.close_rounded,
color: Colors.black,
),
onPressed: () {
getIt<ItemsBloc>().add(UpdateItem(
item: widget.items.elementAt(index).copyWith(
active: !widget.items.elementAt(index).active),
));
setState(() {
widget.items.removeAt(index);
});
if (widget.items.isEmpty) {
Navigator.pop(context);
}
},
),
title: Text(
widget.items.elementAt(index).title,
),
);
},
),
),
],
),
),
),
);
}
}
get_it.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/repository.dart';
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
Future<void> init() async {
// Bloc
getIt.registerLazySingleton<CartBloc>(() => CartBloc(itemRepository: getIt()));
getIt.registerFactory(() => ItemsBloc(
itemRepository: getIt(),
cartBloc: getIt(),
));
// Repository
getIt.registerSingleton<ItemRepository>(ItemRepository());
}
main.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'get_it.dart' as di;
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
di.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => getIt<ItemsBloc>()..add(GetItems()),
),
BlocProvider(
create: (context) => getIt<CartBloc>(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
),
);
}
}
Down Here are the images of the issue:
Ok, so based on the comments, that indicates that you're already in an ItemsLoaded state when removing the item. Since it's not a new state, then a rebuild is not triggered. So it's behaving as expected. Looks like you need to rethink how you do this, perhaps by yielding an ItemsUpdated state before then yielding the ItemsLoaded state.

Flutter: Error when displaying single list value

In Flutter I am reading a file from disk and displaying the list items as a list. Using ListView.builder works fine but with a text widget displaying a single value I get this error. Can someone help?
The error I get is The following RangeError was thrown building MyHomePage(dirty, state: _MyHomePageState#e9932):
RangeError (index): Invalid value: Valid value range is empty: 9
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'dart:async';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
//This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
}
}
______________________________
WITH List.View.builder
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> _names = [];
Future<List<String>> loadNames() async {
List<String> names = [];
await rootBundle.loadString('assets/Stulkur_A.txt').then((q) => {
for (String i in LineSplitter().convert(q)) {names.add(i)}
});
return names;
}
_setup() async {
List<String> names = await loadNames();
setState(() {
_names = names;
});
}
#override
void initState() {
_setup();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Names'),
),
body: Center(
child: Container(
padding: EdgeInsets.all(15),
child: ListView.builder(
itemCount: _names.length,
itemBuilder: (context, index) {
return Text(_names[index]);
})),
),
);
}
}
_____________________
WITH Text widget
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<String> _names = [];
Future<List<String>> loadNames() async {
List<String> names = [];
await rootBundle.loadString('assets/Stulkur_A.txt').then((q) => {
for (String i in LineSplitter().convert(q)) {names.add(i)}
});
return names;
}
_setup() async {
List<String> names = await loadNames();
setState(() {
_names = names;
});
}
#override
void initState() {
_setup();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Names'),
),
body: Center(
child: Container(
padding: EdgeInsets.all(15),
child: Text(_names[9]),
),
),
);
}
}
Try
body: Center(
child: Container(
padding: EdgeInsets.all(15),
child: _names.isEmpty
? CircularProgressIndicator()
: ListView.builder(
itemCount: _names.length,
itemBuilder: (context, index) {
return Text(_names[index]);
},
),
),
),
You should check if _names.length == 0 show a loader or everything you want otherwise show the ListView widget.

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-

Flutter Provider Issue - Changes are not reflecting

Can anyone please let me know why this code is not working? I have one data model file, One file for page where i have a search text and listview to display the names and main file. If i enter something on the search bar, names should be filtered. Please check the code and let me know what i have done wrong.
import 'package:flutter/widgets.dart';
import 'package:flutter/cupertino.dart';
import './class.dart';`enter code here`
class NameList with ChangeNotifier {
List<NameDetails> names = [
NameDetails('2', 'Mahesh'),
NameDetails('1', 'Ganesh'),
NameDetails('3', 'Suresh'),
NameDetails('4', 'Girish'),
];
List<NameDetails> get getNames {
return names;
}
void filterNames(String fil) {
List<NameDetails> names1;
names1 = names.where((n) => n.name.contains(fil));
names=names1;
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:practice/class.dart';
import 'package:provider/provider.dart';
import './data.dart';
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
NameList namesList;
String filterValue;
#override
Widget build(BuildContext context) {
final namesList = Provider.of<NameList>(context);
List<NameDetails> names = namesList.names;
return Scaffold(
appBar: AppBar(
title: TextField(
onChanged: (text) {
namesList.filterNames(text);
},
),
),
body: Column(
children: <Widget>[
Container(
height: 200,
child: Container(
height: 200,
child: ListView.builder(
itemBuilder: (ctx, index) {
return Card(
child: Text('${names[index].name}'),
);
},
itemCount: names.length,
),
),
),
],
));
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './mainScreen.dart';
import './data.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NameList(),
child: MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(),
));
}
}
You can copy paste run full code below
Step 1: You can put filter function in getNames and use List<NameDetails> names = namesList.getNames;
Step 2: filterNames function only update _searchString and do notifyListeners()
code snippet
class NameList with ChangeNotifier {
List<NameDetails> names = ...
String _searchString = "";
UnmodifiableListView<NameDetails> get getNames => _searchString.isEmpty
? UnmodifiableListView(names)
: UnmodifiableListView(
names.where((n) => n.name.contains(_searchString)));
void filterNames(String fil) {
_searchString = fil;
print(_searchString);
notifyListeners();
}
}
...
Widget build(BuildContext context) {
final namesList = Provider.of<NameList>(context);
List<NameDetails> names = namesList.getNames;
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'dart:collection';
class NameDetails {
String id;
String name;
NameDetails(this.id, this.name);
}
class NameList with ChangeNotifier {
List<NameDetails> names = [
NameDetails('2', 'Mahesh'),
NameDetails('1', 'Ganesh'),
NameDetails('3', 'Suresh'),
NameDetails('4', 'Girish'),
];
String _searchString = "";
UnmodifiableListView<NameDetails> get getNames => _searchString.isEmpty
? UnmodifiableListView(names)
: UnmodifiableListView(
names.where((n) => n.name.contains(_searchString)));
void filterNames(String fil) {
_searchString = fil;
print(_searchString);
notifyListeners();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => NameList(),
child: MaterialApp(
title: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MainScreen(),
));
}
}
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
NameList namesList;
String filterValue;
#override
Widget build(BuildContext context) {
final namesList = Provider.of<NameList>(context);
List<NameDetails> names = namesList.getNames;
return Scaffold(
appBar: AppBar(
title: TextField(
onChanged: (text) {
namesList.filterNames(text);
},
),
),
body: Column(
children: <Widget>[
Container(
height: 200,
child: Container(
height: 200,
child: ListView.builder(
itemBuilder: (ctx, index) {
return Card(
child: Text('${names[index].name}'),
);
},
itemCount: names.length,
),
),
),
],
));
}
}