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'),
),
);
}
}