My main objective is to show a CircularProgressIndicator
before I get a location address placename
but I keep getting this error The getter 'placeName' was called on null.
.I did try to check the null
value in futureBuilder
but I believe my implementation is wrong. Could you please take a look ? This is my AppData
class
class AppData extends ChangeNotifier {
Address pickUpLocation;
void updatePickUpLocationAddress(Address pickUpAddress) {
pickUpLocation = pickUpAddress;
notifyListeners();
}
}
and this is the Address
class
class Address {
String placeFormattedAddress;
dynamic placeName;
String placeId;
double latitude;
double longitude;
Address(
{this.placeFormattedAddress,
this.placeName,
this.placeId,
this.latitude,
this.longitude});
}
Now in my MainScreen
I am using it like this but the error persisting.
@override
Widget build(BuildContext context) {
\\\
body: Stack(
\\\
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FutureBuilder (
future: Provider.of < AppData > (context)
.pickUpLocation
.placeName,
builder: (context, snapshot) {
if (snapshot.data == null) {
return CircularProgressIndicator();
} else {
return Text(
Provider.of < AppData > (context)
.pickUpLocation
.placeName,
style: TextStyle(fontSize: 12.0),
overflow: TextOverflow.ellipsis,
);
}
}),
],
),
)
There are a few things to be aware of.
Future
and it isn't wrote to do so. What happens is that your pickUpLocation = pickUpAddress
expression is immediately evaluated, as it was a synchronous operation, ie before the Future
gets converted to an actual value.Provider
to return the Future
you're looking for (that's good), but doing it like that is erroneous since it would basically "mess up" with your Widget tree: say there's an update to your Provider and a Future has completed: what sub-tree should be rendered first?To fix your situation you have to:
Future
;Here's how I'd change your provider model/class:
class AppData extends ChangeNotifier {
Address? pickUpLocation;
Future<void> updatePickUpLocationAddress() async {
// There should be error handling, as well (try-catch-finally clauses)
var pickUpLocation = await Future.delayed(Duration(seconds: 5)); // I'm mocking a request, here
notifyListeners();
}
}
Now, to initialize the Future you either do so in the above Provider (in the Constructor of AppData
), or you have to change your Widget to be Stateful
so that we can access to the initState()
method, in which we can initialize the Future without worrying about multiple calls. In your (now Stateful
) Widget:
var myFuture;
// ...
void initState() {
myFuture = Provider.of<AppData>(context).updatePickUpLocationAddress();
}
// ...
Widget build (BuildContext context) {
return // ...
FutureBuilder(
future: myFuture, // already initialized, won't re-initalize when build() is called
builder: (ctx, snapshot) => // ...
}
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (Provider.of < AppData > (context)
.pickUpLocation ==
null)
CircularProgressIndicator(),
if (Provider.of < AppData > (context)
.pickUpLocation !=
null)
Text(
Provider.of < AppData > (context)
.pickUpLocation
.placeName,
style: TextStyle(fontSize: 12.0),
overflow: TextOverflow.ellipsis,
),
),
)
For anyone who is facing this problem You can easily click on Options+enter on Mac and click on "wrap with Builder" then just pass the context to your future function
child: Builder(
builder: (context) {
return FutureBuilder(
future: futureFunctionHere(context),
builder: (context, AsyncSnapshot snapshot) {
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.