using Provider to avoid rebuild all widget tree in flutter - flutter

as my understand, one of Provider benefits is to avoid rebuild the widget tree, by calling build function, but when i try it practically with this simple example:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:providerexamplelast/counterWidget.dart';
void main() => runApp(ChangeNotifierProvider<Provider1>(
create: (_) => Provider1(),
child: MaterialApp(
home: Counter(),
),
));
int n =0;
class Counter extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("${n++}");
var counter = Provider.of<Provider1>(context);
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: ()=> counter.counter(),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("1"),
SizedBox(height: 5,),
countText(),
SizedBox(height: 5,),
Text("3"),
SizedBox(height: 5,),
Text("4"),
],
),
),
);
}
}
Widget countText(){
return Builder(
builder: (context){
var count = Provider.of<Provider1>(context);
return Text("${count.c}");
},
);
}
by using this part :
print("${n++}");
i noticed that (build) function is recall whenever i press the button and call (counter) function from provider?
so the question here it is just (Stateless) widget, how it rebuild again? and then why i need to use Provider if it is not solve this problem?
Edit: I heard about this way:
var counter = Provider.of<Provider1>(context, listen: false);
so is it solve this problem? and how?

var counter = Provider.of<Provider1>(context, listen: false);
The Provider is one of state management mechanism which flutter have, under the hood, Provider keeps track of the changes which are done inside the widget. It doesn't matter to Provider whether it's a Stateless widget or Stateful widget, It's gonna rebuild widget if anything gets change.
listen: false tells the Provider not to rebuild widget even if data gets modified.

Related

Update List Item After Delete Action in Flutter

Hello I am new to flutter and I have a problem to update the list after executing a deleting an item from the database. Some said to use setState, but I still don't know how to implement it in my code. Tried to call seState right after the delete action, but still nothing happened. Still have some trouble to understand which component to update in Flutter. Thank you.
class ProfileView extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return _ProfileViewState();
}
}
class _ProfileViewState extends State<ProfileView> {
late Future<List<Patient>> _patients;
late PatientService patientService;
#override
void initState() {
super.initState();
patientService = PatientService();
_patients = patientService.getPatient();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Profile')),
body: Column(
children: <Widget>[
Flexible(
child: SizedBox(
child: FutureBuilder<List<Patient>>(
future: _patients,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if(snapshot.hasError) {
print(snapshot);
return Center(
child: Text("Error"),
);
} else if (snapshot.hasData){
List<Patient> patients = snapshot.data;
return _buildListView(patients);
} else {
return Center(
child: Container(),
);
}
},
),
),
)
],
),
);
}
Widget _buildListView(List<Patient> patients) {
return ListView.separated(
separatorBuilder: (BuildContext context, int i) => Divider(color: Colors.grey[400]),
itemCount: patients.length,
itemBuilder: (context, index) {
Patient patient = patients[index];
return Row(
children: <Widget>[
Flexible(
child: SizedBox(
child: ListTile(
title: Text(patient.firstName),
subtitle: Text(patient.phone),
trailing: IconButton(
icon: new Icon(const IconData(0xf4c4, fontFamily: 'Roboto'), size: 48.0, color: Colors.red),
onPressed: () {
patientService.deletePatient(patient.id.toString());
}),
),
)
)
],
);
}
);
}
}
You can achieve that by removing the initialization of the future from the initState and simply give the future of patientService.getPatient() to the FutureBuilder, like this:
future: patientService.getPatient()
And call setState after making sure the patient have been successfully deleted.
The explanation behind doing that is when you delete your patient from the DB, yes it is removed from there, but the UI didn't get an update about the list of patients after the delete. And the reason why calling setState in your case doesn't make a change is because you are assigning the future in initState which is called once and only once when the widget is initialized. So when you call setState the future won't be called again hence no new data is fetched.
So what I did is just remove the initialization of the future from initState and give it to the FutureBuilder, which will be rebuild whenever you call setState.
Even though this works, it isn't the ideal solution. Because you are rebuilding your whole widget every time a delete is made which is kinda of heavy considering the FutureBuilder, so what I suggest is checking out some state mangement solutions like Bloc or Mobx or even the Provider package (which isn't a state mangement according to its creator).
Hope that makes clear !
Happy coding !
call setState() inside the onPressed method.
onPressed: () {
patientService.deletePatient(patient.id.toString());
setState((){});
}),
If you are not saving a local copy of the list from which you are deleting an item then this works
Although if the delete method deletes on from wherever you are fetching the items then you will need to call
_patients = patientService.getPatient();
before calling setState()
I think your deletePatient is asynchronous method. And you are calling this method without await and after this function setState is called thus widget is getting updated before delete is completed.
If deletePatient is asynchronous then add await before calling it and add setState after it.
onPressed: () async {
await patientService.deletePatient(patient.id.toString());
setState((){});
})

Animating between provider stream changes

I have a pageview in flutter with 5 pages, each with its own scaffold. All states are managed through providers of streams or values. I have a stream that has it's own built in method which passes InternetConnected.connected or disconnected. When the internet connection is lost, I want to load a separate UI in the specific page which shows internet connection lost instead of the widgets that were previously present in the scaffold.
How I'm doing it now (pseudo code):
class ConnectionScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
final connection = Provider.of<InternetStatus>(context);
detectConnection () {
if (connection.InternetConnection == InternetConnection.connected)
return Container(); // Returns UI when internet on
else
return Container(); // Returns disconnected UI
}
return Scaffold(body: detectConnection());
}
Two Questions:
I want to animate the transition between the two states ie. Connected and Disconnected with the disconnection screen flowing down from the top of the display and vice versa. What is the correct way to do that with provider state management? Right now it just rebuilds instantaneously, which is not very 'pretty'.
Since Provider.of<> does not allow granular rebuilds in case the stream value changes, how would I make it so that other properties 'provided' by provider are handled better? I know about Consumer and Selector, but those are rebuilding the UI too...
Thanks in advance
Animation
An AnimatedSwitcher widget (included in SDK, not related to Provider) might suffice to animate between your two widgets showing connected / disconnected states. (AnimatedContainer might work as well if you're just switching a color or something else in the constructor argument list of Container.)
A key is needed for the child of AnimatedSwitcher when the children are of the same class, but differ internally. If they're completely different Types, Flutter knows to animate between the two, but not if they're the same Type. (Has to do with how Flutter analyzes the widget tree looking for needed rebuilds.)
Rebuild Only Affected Widgets
In the example below, the YellowWidget isn't being rebuilt, and neither is its parent. Only the Consumer<InternetStatus> widget is being rebuilt when changing from Connected to Disconnected statuses in the example.
I'm not an expert on Provider and I find it easy to make mistakes in knowing which Provider / Consumer / Selector / watcher to use to avoid unnecessary rebuilds. You might be interested in other State Management solutions like Get, or RxDart+GetIt, etc. if Provider doesn't click for you.
Note: an extra Builder widget is used as parent to the child of ChangeNotifierProvider, to make everything underneath a child. This allows InheritedWidget to function as intended (the base upon which Provider is built). Otherwise, the child of ChangeNotifierProvider would actually share its context and be its sibling, not descendant.
i.e. They'd both get the context shown here:
class ProviderGranularPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
This is also a tricky nuance of Flutter. If you wrap your entire MaterialApp or MyApp widget in Provider, this extra Builder is obviously unnecessary.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class InternetStatus extends ChangeNotifier {
bool connected = true;
void setConnect(bool _connected) {
connected = _connected;
notifyListeners();
}
}
/// Granular rebuilds using Provider package
class ProviderGranularPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<InternetStatus>(
create: (_) => InternetStatus(),
child: Builder(
builder: (context) {
print('Page (re)built');
return SafeArea(
child: Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Expanded(
flex: 3,
child: Consumer<InternetStatus>(
builder: (context, inetStatus, notUsed) {
print('status (re)built');
return AnimatedSwitcher(
duration: Duration(seconds: 1),
child: Container(
key: getStatusKey(context),
alignment: Alignment.center,
color: getStatusColor(inetStatus),
child: getStatusText(inetStatus.connected)
),
);
},
),
),
Expanded(
flex: 3,
child: YellowWidget(),
),
Expanded(
flex: 1,
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Connect'),
onPressed: () => setConnected(context, true),
),
RaisedButton(
child: Text('Disconnect'),
onPressed: () => setConnected(context, false),
)
],
),
),
)
],
),
),
);
},
),
);
}
/// Show other ways to access Provider State, using context & Provider.of
Key getStatusKey(BuildContext context) {
return ValueKey(context.watch<InternetStatus>().connected);
}
void setConnected(BuildContext context, bool connected) {
Provider.of<InternetStatus>(context, listen: false).setConnect(connected);
}
Color getStatusColor(InternetStatus status) {
return status.connected ? Colors.blue : Colors.red;
}
Widget getStatusText(bool connected) {
String _text = connected ? 'Connected' : 'Disconnected';
return Text(_text, style: TextStyle(fontSize: 25));
}
}
class YellowWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('Yellow was (re)built');
return Container(
color: Colors.yellow,
child: Center(
child: Text('This should not rebuild'),
),
);
}
}

Managing state in Flutter using Provider

I'm trying to implement Provider state management on counter application to understand Provider's functionality better. I have added two buttons with respect to two different text widget. So, now whenever I click any of the two widget both the Text widgets get update and give same value. I want both the widgets independent to each other.
I have used ScopedModel already and got the desire result but now I want to try with provider.
Image Link : https://i.stack.imgur.com/ma3tR.png
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("====Home Page Rebuilt====");
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment:CrossAxisAlignment.center,
children: [
Consumer<CounterModel>(
builder: (context, value, child) {
return CustomWidget(
number: value.count.toString(),
);
},
),
Consumer<CounterModel>(
builder: (context, value, child) {
return CustomWidget(
number: value.count.toString(),
);
},
),
],
)),
);
}
}
class CustomWidget extends StatelessWidget {
final String number;
const CustomWidget({Key key, this.number}) : super(key: key);
#override
Widget build(BuildContext context) {
print("====Number Page Rebuilt====");
return ButtonBar(
alignment: MainAxisAlignment.center,
children: [
Consumer<CounterModel>(
builder: (context, value, child) {
return Text(
value.count.toString(),
style: Theme.of(context).textTheme.headline3,
);
},
),
FlatButton(
color: Colors.blue,
onPressed: () =>
Provider.of<CounterModel>(context, listen: false).increment(),
child: Text("Click"),
),
],
);
}
}
If you want them independent from each other, then you need to differentiate them somehow. I have a bit of a different style to implement the Provider and it hasn't failed me yet. Here is a complete example.
You should adapt your implementation to something like this:
Define your provider class that extends ChangeNotifier in a CounterProvider.dart file
import 'package:flutter/material.dart';
class CounterProvider extends ChangeNotifier {
/// You can either set an initial value here or use a UserProvider object
/// and call the setter to give it an initial value somewhere in your app, like in main.dart
int _counter = 0; // This will set the initial value of the counter to 0
int get counter => _counter;
set counter(int newValue) {
_counter = newValue;
/// MAKE SURE YOU NOTIFY LISTENERS IN YOUR SETTER
notifyListeners();
}
}
Wrap your app with a Provider Widget like so
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// don't forget to import it here too
import 'package:app/CounterProvider.dart';
void main() {
runApp(
MaterialApp(
initialRoute: '/root',
routes: {
'/root': (context) => MyApp(),
},
title: "Your App Title",
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
/// Makes data available to everything below it in the Widget tree
/// Basically the entire app.
ChangeNotifierProvider<CounterProvider>.value(value: CounterProvider()),
],
child: MaterialApp(
home: HomeScreen(),
),
);
}
}
Access and update data anywhere in the app
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
/// MAKE SURE TO IMPORT THE CounterProvider.dart file
import 'package:app/CounterProvider.dart';
class HomeScreen extends StatefulWidget {
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
CounterProvider counterProvider;
#override
Widget build(BuildContext context) {
/// LISTEN TO THE CHANGES / UPDATES IN THE PROVIDER
counterProvider = Provider.of<CounterProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text("HomePage"),
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
//crossAxisAlignment:CrossAxisAlignment.center,
children: [
_showCounterButton(1),
_showCounterButton(2),
],
),
),
);
}
Widget _showCounterButton(int i) {
return ButtonBar(
alignment: MainAxisAlignment.center,
children: [
Text(
i == 1
? counterProvider.counter1.toString()
: counterProvider.counter2.toString(),
style: Theme.of(context).textTheme.headline3,
),
FlatButton(
color: Colors.blue,
onPressed: () {
/// UPDATE DATA IN THE PROVIDER. BECAUSE YOU're USING THE SETTER HERE,
/// THE LISTENERS WILL BE NOTIFIED AND UPDATE ACCORDINGLY
/// you can do this in any other file anywhere in the Widget tree, as long as
/// it it beneath the main.dart file where you defined the MultiProvider
i == 1
? counterProvider.counter1 += 1
: counterProvider.counter2 += 1;
setState(() {});
},
child: Text("Click"),
),
],
);
}
}
If you want, you can change the implementation a bit. If you have multiple counters, for multiple widgets, then just create more variables in the CounterProvider.dart file with separate setters and getters for each counter. Then, to display/update them properly, just use a switch case inside the _showCounterButton() method and inside the onPressed: (){ switch case here, before setState((){}); }.
Hope this helps and gives you a better understanding of how Provider works.

Are obs stream being closed automatically by GetxControllers?

I am using the following package https://pub.dev/packages/get. Do I need to close my .obs in the onClose of a GetxController? I can't find anything about this in the docs. And looking at my memory it appears that the are being destroyed automatically.
In my understanding of GetX + Flutter so far...
No, you shouldn't have to remove .obs in the close() method of GetxControllers. Disposal of observables from a Controller are done automatically when the Controller is removed from memory.
GetX disposes/removes GetxControllers (and their observables) when the widget in which they are contained are popped off the widget stack / removed from the widget tree (by default, but can be overridden).
You can see this in the override of dispose() methods of various Get widgets.
Here's a snippet of dispose() that's run when GetX widgets are popped/removed:
#override
void dispose() {
if (widget.dispose != null) widget.dispose(this);
if (isCreator || widget.assignId) {
if (widget.autoRemove && GetInstance().isRegistered<T>(tag: widget.tag)) {
GetInstance().delete<T>(tag: widget.tag);
}
}
subs.cancel();
_observer.close();
controller = null;
isCreator = null;
super.dispose();
}
When you use Bindings or Get.to() you're using GetPageRoute's which do cleanup by Route names:
#override
void dispose() {
if (Get.smartManagement != SmartManagement.onlyBuilder) {
WidgetsBinding.instance.addPostFrameCallback((_) => GetInstance()
.removeDependencyByRoute("${settings?.name ?? routeName}"));
}
super.dispose();
}
Test App
Below is a test App you can copy/paste into Android Studio / VSCode and run to watch the debug or run window output for GETX lifecycle events.
GetX will log the creation & disposal of Controllers in and out of memory.
The app has a HomePage and 3 ChildPages using Get Controllers in 3 ways, all which remove itself from memory:
GetX / GetBuilder
Get.put
Bindings
import 'package:flutter/material.dart';
import 'package:get/get.dart';
void main() {
// MyCounterBinding().dependencies(); // usually where Bindings happen
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'GetX Dispose Ex',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GetX Dispose Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('GetX/Builder Child'),
onPressed: () => Get.to(ChildPage()),
),
RaisedButton(
child: Text('Get.put Child'),
onPressed: () => Get.to(ChildPutPage()),
),
RaisedButton(
child: Text('Binding Child'),
onPressed: () => Get.to(ChildBindPage()),
),
],
),
),
);
}
}
/// GETX / GETBUILDER
/// Creates Controller within the Get widgets
class ChildPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('GetX Dispose Test Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('This is the Child Page'),
GetX<ChildX>(
init: ChildX(),
builder: (cx) => Text('Counter: ${cx.counter}', style: TextStyle(fontSize: 20),),
),
GetBuilder<ChildX>(
init: ChildX(),
builder: (cx) => RaisedButton(
child: Text('Increment'),
onPressed: cx.inc,
),
),
],
),
),
);
}
}
/// GET.PUT
/// Creates Controller instance upon Build, usable anywhere within the widget build context
class ChildPutPage extends StatelessWidget {
//final ChildX cx = Get.put(ChildX()); // wrong place to put
// see https://github.com/jonataslaw/getx/issues/818#issuecomment-733652172
#override
Widget build(BuildContext context) {
final ChildX cx = Get.put(ChildX());
return Scaffold(
appBar: AppBar(
title: Text('GetX Dispose Test Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('This is the Child Page'),
Obx(
() => Text('Counter: ${cx.counter}', style: TextStyle(fontSize: 20),),
),
RaisedButton(
child: Text('Increment'),
onPressed: cx.inc,
)
],
),
),
);
}
}
class MyCounterBinding extends Bindings {
#override
void dependencies() {
Get.lazyPut(() => ChildX(), fenix: true);
}
}
/// GET BINDINGS
/// Normally the MyCounterBinding().dependencies() call is done in main(),
/// making it available throughout the entire app.
/// A lazyPut Controller /w [fenix:true] will be created/removed/recreated as needed or
/// as specified by SmartManagement settings.
/// But to keep the Bindings from polluting the other examples, it's done within this
/// widget's build context (you wouldn't normally do this.)
class ChildBindPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
MyCounterBinding().dependencies(); // just for illustration/example
return Scaffold(
appBar: AppBar(
title: Text('GetX Dispose Test Counter'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text('This is the Child Page'),
Obx(
() => Text('Counter: ${ChildX.i.counter}', style: TextStyle(fontSize: 20),),
),
RaisedButton(
child: Text('Increment'),
onPressed: ChildX.i.inc,
)
],
),
),
);
}
}
class ChildX extends GetxController {
static ChildX get i => Get.find();
RxInt counter = 0.obs;
void inc() => counter.value++;
}
Notes
Get.to vs. Navigator.push
When using Get.put() in a child widget be sure you're using Get.to() to navigate to that child rather than Flutter's built-in Navigator.push.
GetX wraps the destination widget in a GetPageRoute when using Get.to. This Route class will dispose of Controllers in this route when navigating away / popping the widget off the stack. If you use Navigator.push, GetX isn't involved and you won't get this automatic cleanup.
Navigator.push
onPressed: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => ChildPutPage())),
Get.to
onPressed: () => Get.to(ChildPutPage()),
Based from the code of the super implementation of onClose, by default it does nothing currently.
https://github.com/jonataslaw/getx/blob/7146b6a53c0648104e4f623385deaff055e0036a/lib/get_instance/src/lifecycle.dart#L56
And from the comments, it says:
/// Called before [onDelete] method. [onClose] might be used to
/// dispose resources used by the controller. Like closing events,
/// or streams before the controller is destroyed.
/// Or dispose objects that can potentially create some memory leaks,
/// like TextEditingControllers, AnimationControllers.
/// Might be useful as well to persist some data on disk.
void onClose() {}
from that I think you need to manually close your streams in YourController::onClose() override function.
It appears you can use obs safely when using GetWorkers. Run this code and you'll notice that when you click the buttons a few time there will only be one print per page switch.
void main(){
runApp(GetMaterialApp(home: TestWidget(),));
}
class TestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('next'),
onPressed: () => Get.to<SomeWidget>(SomeWidget()),
),
);
}
}
class SomeWidget extends StatelessWidget {
RxBool isSubscribed = false.obs;
SomeWidget() {
ever(isSubscribed, (_) => print('test'));
}
#override
Widget build(BuildContext context) {
return Center(
child: RaisedButton(
child: Text('back'),
onPressed: () {
isSubscribed.value = !isSubscribed.value;
Get.back();
},
),
);
}
}

Flutter Provider doesn't rebuild widget

here's a piece of my code,
but please read the question before jumping to conclusions:
class RescheduleCard extends StatelessWidget {
#override
Widget build(BuildContext context)=> Consumer<AppSnapshot>(
builder: (context, appSnapshot, _)=>
(appSnapshot.reschedule!=null)
?RescheduleBuilder(
model: appSnapshot.reschedule,
controllers: appSnapshot.controllers,
)
:Carouselcard(
title: carouselPageTitle(title: rescheduleTitle,test: appSnapshot.test),
colors: yellows,
so I'm sort of new to provider and I'm used to Bloc,
my api once receive some call the provider and set a model;
edit: here's the provider 256 lines tripped down to what concern
the "reschedule"...
class AppSnapshot with ChangeNotifier {
RescheduleModel _reschedule;
RescheduleModel get reschedule => _reschedule;
void cleanReschedule() {
reschedule=null;
notifyListeners();
}
set reschedule(RescheduleModel reschedule) {
_reschedule=reschedule;
notifyListeners();
}
}
re-edit: on top of everything:
void main() async {
final AppSnapshot _appSnapshot = AppSnapshot();
await _appSnapshot.load();
runApp(ChangeNotifierProvider(
builder: (context) => _appSnapshot,
child: Initializer()));
}
I'm expecting the "Consumer" to rebuild my widget,
but doesn't!
I'm sure the data is there, because the widget is inside a carousel
and as soon as I move it it rebuilds properly.
what am I missing?
thank you
re-re-edit: here's an example of another Carousel Card where provider works quilckly and changes apply in realtime
Consumer<AppSnapshot>(
builder: (context, appSnapshot, _)=> Carouselcard(
title: carouselPageTitle(title: optionsTitle,test: appSnapshot.test),
colors: lightBlues,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
((appSnapshot.id??'')!='') // ID WIDGET
? ListTile(
leading: CustomIcon(
icon: deleteIcon,
onTap: () => appSnapshot.deleteID(),
),
title: _customCard(onTap:()=> _showID(id: appSnapshot.id,context: context)),
subtitle: Text(tap2delete,textScaleFactor:0.8),
)
: IdTile(),
According to this article the FutureProvider does not listen for changes, it will only rebuild Consumers 1 time, when Future is complete.
The article even explains how the author tested this.
The developer of the Dart Provider package also explains the use case for FutureProvider.
Specificies the type in main.dart
void main() async {
final AppSnapshot _appSnapshot = AppSnapshot();
await _appSnapshot.load();
runApp(ChangeNotifierProvider< **AppSnapshot** >(
builder: (context) => _appSnapshot,
child: Initializer()));
}