I'm using provider Flutter package.
I have a main list and each item of the main list has a sublist. I can add items to the main list and it's working but every time I add something to the sublist the UI is not getting updated. Run the code and add some items and open them then add some items there (this is the sublist) and you will see you won't see any update but if you minimize the keyboard the UI will get updated. Maybe because I am using Provider.of<Collection>(context)
to access the object of the sublist and notifyListeners()
of the CityName
class can't run the findById(id)
this is what I can think of.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: Collection(),
),
ChangeNotifierProvider.value(
value: CityName(),
)
],
child: MaterialApp(
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ListView(
children: Provider.of<Collection>(context)
.cityNameCollection
.map((collection) {
return CollectionCard(collection.title, collection.id);
}).toList()),
),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _textEditingController,
autofocus: true,
),
),
FlatButton(
onPressed: () {
Provider.of<Collection>(context)
.addCollection(_textEditingController.text);
_textEditingController.clear();
},
child: Text(
'ADD',
),
),
],
),
),
]),
);
}
}
class CollectionCard extends StatelessWidget {
final String title;
final String id;
CollectionCard(this.title, this.id);
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 20),
child: Text(title),
),
),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => CityNamesList(id),
),
);
},
);
}
}
class CityNamesList extends StatelessWidget {
final String id;
CityNamesList(this.id);
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
final item = Provider.of<Collection>(context).findById(id);
return Scaffold(
appBar: AppBar(
title: Text(item.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ListView(
children: item.names.map((name) {
return ListTile(
title: Text(name),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
item.removeName(name);
},
),
);
}).toList()),
),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _textEditingController,
autofocus: true,
),
),
FlatButton(
onPressed: () {
item.addName(_textEditingController.text);
_textEditingController.clear();
},
child: Text(
'ADD',
),
),
],
),
),
],
),
);
}
}
class Collection with ChangeNotifier {
List<CityName> cityNameCollection = [];
void addCollection(value) {
cityNameCollection
.add(CityName(title: value, id: DateTime.now().toString()));
notifyListeners();
}
CityName findById(id) {
return cityNameCollection.firstWhere((a) => a.id == id);
}
}
class CityName with ChangeNotifier {
String title;
String id;
List<String> names = [];
CityName({this.title, this.id});
void addName(value) {
names.add(value);
notifyListeners();
}
void removeName(value) {
names.remove(value);
notifyListeners();
}
}
There are several problems here
1) You are creating Collection()
inside ChangeNotifierProvider.value
which can lead to recreating it (not just now but after some changes in code). Instead use ChangeNotifierProvider
with builder
which will create the value exactly once
2) You are using single ChangeNotifierProvider
for CityName
while there can be multiple CityName
instances. So instead of referring to specific instance of CityName
all providers will always give you the same one
3) Passing collection.id
and then finding it by id is not necessary and complicates the code. Just pass the collection instance itself
4) CityNamesList
dosen't get provided specific CityName
but gets it from Collection
provider. In this case the UI will not be rebuilt because it does not listen to CityNamesList
changes. Actually it listens to Collection
changes but Collection
doesn't know when its item changes because no one calls notifyListeners()
on Collection
inside CityName@addName
5) Finally, CityName
's constructor fields are optional (not required) which allows to create dummy instances and leads to errors
Here is an example how you can fix this problems (without removeName
for simplicity)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
builder: (context) => Collection(),
child: MaterialApp(
home: MyHomePage(),
),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ListView(
children: Provider.of<Collection>(context)
.cityNameCollection
.map((item) => CollectionCard(item))
.toList(),
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _textEditingController,
autofocus: true,
),
),
FlatButton(
onPressed: () {
Provider.of<Collection>(context)
.addCollection(_textEditingController.text);
_textEditingController.clear();
},
child: Text('ADD'),
),
],
),
],
),
);
}
}
class CollectionCard extends StatelessWidget {
final CityName item;
CollectionCard(this.item);
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Card(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 20),
child: Text(item.title),
),
),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return ChangeNotifierProvider.value(
value: item,
child: CityNamesList(),
);
},
),
);
},
);
}
}
class CityNamesList extends StatelessWidget {
final _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
final item = Provider.of<CityName>(context);
return Scaffold(
appBar: AppBar(
title: Text(item.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: ListView(
children: item.names.map((name) {
return ListTile(
title: Text(name),
);
}).toList(),
),
),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: _textEditingController,
autofocus: true,
),
),
FlatButton(
onPressed: () {
item.addName(_textEditingController.text);
_textEditingController.clear();
},
child: Text('ADD'),
),
],
),
),
],
),
);
}
}
class Collection with ChangeNotifier {
List<CityName> cityNameCollection = [];
void addCollection(String value) {
cityNameCollection.add(CityName(title: value));
notifyListeners();
}
}
class CityName with ChangeNotifier {
String title;
List<String> names = [];
CityName({@required this.title});
void addName(value) {
names.add(value);
notifyListeners();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With