OK, I'm stuck on this problem using FutureProvider.
I already create a FutureProvider above my MaterialApp, so it should be recognized right?
My widget tree is somewhat like this:
MyApp >> Home >> CardVehicle
Here's my main.dart code where I create object Provider:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final Service service = Service();
@override
Widget build(BuildContext context) {
return FutureProvider(
create: (_) => service.fetchCarYear(),
catchError: (_, error) => print(error),
child: MaterialApp(
title: 'KPM Demo',
theme: ThemeData(
primarySwatch: Colors.amber,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Home(),
),
);
}
}
my dummy Service class:
class Service {
Future<CarYear> fetchCarYear() async {
CarYear carYear = CarYear();
final response = await Future.delayed(
Duration(milliseconds: 500),
() => jsonEncode({
"data": [
{"year": "2020"},
{"year": "2019"},
{"year": "2018"}
]
}),
);
carYear = CarYear.fromJson(jsonDecode(response));
return carYear;
}
}
Here's where I put my Provider:
class CardVehicle extends StatelessWidget {
@override
Widget build(BuildContext context) {
CarYear carYear = Provider.of<CarYear>(context);
return Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
children: <Widget>[
DropdownButton(
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down),
items: carYear.data
.map((item) => DropdownMenuItem(child: Text(item.year)))
.toList() ?? null,
onChanged: null,
),
],
),
);
}
}
Did I make a mistake somewhere? Please help!
Edit: here's my CarYear class:
class CarYear {
List<Data> data;
CarYear({this.data});
CarYear.fromJson(Map<String, dynamic> json) {
if (json['data'] != null) {
data = new List<Data>();
json['data'].forEach((v) {
data.add(new Data.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.data != null) {
data['data'] = this.data.map((v) => v.toJson()).toList();
}
return data;
}
}
class Data {
String year;
Data({this.year});
Data.fromJson(Map<String, dynamic> json) {
year = json['year'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['year'] = this.year;
return data;
}
}
it is better you do it with a ChangeNotifierProvider
in the main you created with MultiProvider
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => Service()),
],
child: MaterialApp(..)
)
}
in the Service class i added extends ChangeNotifiere
when notifyListeners()
is calling, all Consumers are updated
class Service extends ChangeNotifier {
// Variable set in Service
CarYear _carYear = new CarYear();
CarYear get carYear => _carYear;
// init MultiProvider create() =>
Service() {
fetchCarYear();
}
Future<CarYear> fetchCarYear() async {
_carYear = CarYear();
final response = await Future.delayed(
Duration(milliseconds: 500),
() => jsonEncode({
"data": [
{"year": "2020"},
{"year": "2019"},
{"year": "2018"}
]
}),
);
// update _carYear Variable
_carYear = CarYear.fromJson(jsonDecode(response));
// all Consumer get a refresh()
notifyListeners();
}
}
in CardVehicle i placed a Consumer but CarYear carYear = Provider.of<Service>(context).carYear
is also working.. but with Provider.of you update every notifyListeners()
the whole widget
class CardVehicle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
children: <Widget>[
// its better you do it here with a Consumer, because not the whole widget is updating
new Consumer<Service>(
builder: (context, service, child) => new DropdownButton(
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down),
items: service.carYear.data
.map((item) => DropdownMenuItem(child: Text(item.year), value: item.year,))
.toList(),
onChanged: (value) {
print(value);
},
),
),
],
),
);
}
}
Comming from the other question you posted.
You need to change the catchError
of your futureProvider
to return a CarYear instead of void. Now the futureProvider
is of type Dynamic and provider.of
is searching not for a provider of dynamic , but a provider of CarYear .
To avoid this, make catchError
return an empty CarYear or make it throw an exception.
I hope this helps!
Example only with FutureProvider
MultiProvider(
providers: [
FutureProvider(create: (_) => Service().fetchCarYear()),
],
child: MaterialApp(..)
Service Class with return CarYear
class Service {
// Variable set in Service
CarYear _carYear;
Future<CarYear> fetchCarYear() async {
_carYear = CarYear(data: new List());
final response = await Future.delayed(
Duration(milliseconds: 500),
() => jsonEncode({
"data": [
{"year": "2020"},
{"year": "2019"},
{"year": "2018"}
]
}),
);
// update _carYear Variable
_carYear = CarYear.fromJson(jsonDecode(response));
// all Consumer get a refresh()
//notifyListeners();
return _carYear;
}
}
and the CardVehicle with a CarYear carYear = Provider.of<CarYear>(context);
class CardVehicle extends StatelessWidget {
@override
Widget build(BuildContext context) {
CarYear carYear = Provider.of<CarYear>(context);
return Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.white,
),
child: Column(
children: <Widget>[
new DropdownButton(
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down),
items: carYear != null ? carYear.data
.map((item) => DropdownMenuItem(child: Text(item.year), value: item.year,))
.toList() : null,
onChanged: (value) {
print(value);
},
),
],
),
);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.