Simple Riverpod example beyond the counter app: sum of two cells - flutter

I'm learning Flutter and I'm stuck on state management. I took a look at Riverpod and it looks promising, but I have a hard time to go beyond the counter app to something more complicated.
For example, I want to have two TextFields that collect numbers, and another Text widget to display the sum of the two TextField values. Here's what I have.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
void main() {
runApp(ProviderScope(
child: MyApp(),
));
}
class MyApp extends HookWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(title: 'Adding two cells'),
);
}
}
final cellProvider = StateProvider((_) => <int>[0, 0]);
class MyHomePage extends HookWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Consumer(builder: (context, watch, _) {
print(watch(cellProvider).state);
num _sum = watch(cellProvider).state[0] + watch(cellProvider).state[1];
return Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Cell(0),
Cell(1),
],
),
SizedBox(
height: 100,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Sum: ${_sum.toString()}'),
],
),
],
),
);
}),
);
}
}
class Cell extends HookWidget {
Cell(this.index);
final int index;
#override
Widget build(BuildContext context) {
return Container(
width: 150,
padding: EdgeInsets.all(30),
child: TextField(
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
onChanged: (value) {
context.read(cellProvider).state[index] = num.tryParse(value);
},
),
);
}
}
The Text widget does not update. Any suggestion?
Thank you very much,
Tony

The provider only updates when the object it provides changes, just as a Stream returns a final value you need to update the whole object (List<int>) so the consumer updates properly, changing inner values of an iterable won't trigger an update
onChanged: (value) {
final List<int> myList = context.read(cellProvider).state;
myList[index] = num.tryParse(value);
context.read(cellProvider).state = myList; //update the state with a new list
},

Related

Flutter: How To Change/Refresh State From Another Widget

I am trying to have a global integer that is displayed in a widget and then is updated by something (a button click or something) from another widget. All of the other ways i have tried don't work. What is the best way to do this?
Stack overflow says i have too much code so more text more text more text
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScoreDisplay(),
PointButton(),
],
),
),
),
),
);
}
int score = 0;
class ScoreDisplay extends StatefulWidget {
#override
_ScoreDisplayState createState() => _ScoreDisplayState();
}
class _ScoreDisplayState extends State<ScoreDisplay> {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text(
'Score: $score',
),
),
);
}
}
class PointButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: RaisedButton(
//onPressed: //increment score,
),
),
);
}
}
You need to implement some kind of State Management for that.
Here are two basic ways to implement such a feature: with a StatefulWidget and with Riverpod.
1. With a StatefulWidget
I extracted your Scaffold as a StatefulWidget maintaining the score of your application.
I then use ScoreDisplay as a pure StatelessWidget receiving the score as a parameter. And your PointButton is also Stateless and call the ScorePage thanks to a simple VoidCallback function.
Full source code:
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: ScorePage(),
),
);
}
class ScorePage extends StatefulWidget {
const ScorePage({
Key key,
}) : super(key: key);
#override
_ScorePageState createState() => _ScorePageState();
}
class _ScorePageState extends State<ScorePage> {
int score = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScoreDisplay(score: score),
PointButton(onIncrement: () => setState(() => score++)),
],
),
),
);
}
}
class ScoreDisplay extends StatelessWidget {
final int score;
const ScoreDisplay({Key key, this.score}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Text(
'Score: $score',
),
),
);
}
}
class PointButton extends StatelessWidget {
final VoidCallback onIncrement;
const PointButton({Key key, this.onIncrement}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: ElevatedButton(
onPressed: () => onIncrement?.call(),
child: Text('CLICK ME'),
),
),
);
}
}
2. With Riverpod
Create a StateProvider:
final scoreProvider = StateProvider<int>((ref) => 0);
Watch the StateProvider:
final score = useProvider(scoreProvider).state;
Update the StateProvider
context.read(scoreProvider).state++
Full Source Code
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() {
runApp(
ProviderScope(
child: MaterialApp(
home: Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ScoreDisplay(),
PointButton(),
],
),
),
),
),
),
);
}
int score = 0;
class ScoreDisplay extends HookWidget {
#override
Widget build(BuildContext context) {
final score = useProvider(scoreProvider).state;
return Center(
child: Container(
child: Text(
'Score: $score',
),
),
);
}
}
class PointButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Center(
child: Container(
child: ElevatedButton(
onPressed: () => context.read(scoreProvider).state++,
child: Text('CLICK ME'),
),
),
);
}
}
final scoreProvider = StateProvider<int>((ref) => 0);
Check Riverpod Website for more info and more advanced use cases.
But you have many more flavors of State Management available.
The best example is to use "provider" package which can be found on www.pub.dev
It is very easy state management package that can help You solve this problem. Keep in my that provider instead of setState() uses notifyListener()

Is it possible to initiate a 2 dimensional array of SizedBox (as an example, I need some kind of square) in Flutter

I want to program a chess game in Flutter so first I need to make my own board. For that, I thought I can initiate a 2 dimension array of SizedBox-es (again, not necessarily) and color it just like a real chess board.
But is it possible?
You can copy paste run full code below
You can directly use package https://pub.dev/packages/flutter_chess_board or reference it's source code
code snippet of buildChessBoard
https://github.com/deven98/flutter_chess_board/blob/c8042f2aa499158c10b87aca339a9a19198ce2f3/lib/src/chess_board.dart#L182
var whiteSquareList = [
[
"a8",
"b8",
"c8",
"d8",
"e8",
"f8",
"g8",
"h8",
...
Widget buildChessBoard() {
return Column(
children: widget.whiteSideTowardsUser
? whiteSquareList.map((row) {
return ChessBoardRank(
children: row,
);
}).toList()
: whiteSquareList.reversed.map((row) {
return ChessBoardRank(
children: row.reversed.toList(),
);
}).toList(),
);
}
...
class ChessBoardRank extends StatelessWidget {
/// The list of squares in the rank
final List<String> children;
ChessBoardRank({this.children});
#override
Widget build(BuildContext context) {
return Expanded(
flex: 1,
child: Row(
children: children
.map(
(squareName) => BoardSquare(squareName: squareName),
)
.toList(),
),
);
}
}
working demo
full example code
import 'package:flutter/material.dart';
import 'package:flutter_chess_board/src/chess_board.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ChessBoard(
onMove: (move) {
print(move);
},
onCheckMate: (color) {
print(color);
},
onDraw: () {},
size: MediaQuery.of(context).size.width,
enableUserMoves: true,
)
],
),
),
);
}
}
A better option is to add a gridView like this:
GridView.builder(
itemCount: 64,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 8,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0),
itemBuilder: (BuildContext context, int index) {
return Container(
color: index%2 == 0 ? Colors.white : Colors.black
);
},
)
If you have SizedBox instead, it will be difficult for you to add color, coin image, and alignment etc

Flutter: Detect rebuild of any widget which is not visible on screen but is in the widget tree

Summary:
As showing a page/route using the Navigator, a new branch is created from the nearest MaterialApp parent. Meaning both pages (Main & New) will be in memory and will rebuild if they are listening to the same ChangeNotifier.
I am having trouble finding out which widget is on-screen currently visible to the user.
I need this to handle a scenario to skip performing asynchronous or long processes with some side effects, from a widget that might be in the widget tree but currently not visible.
Note: The sample code given here represents the basic architecture of the app I am currently working on, but reproduces the exact problem.
I am having this problem with a very different and complex widget tree that I have in my app, executing the doLongProcess() from a widget that is not visible on the screen. Also doLongProcess() changes some common property in my app which causes an issue, as any background widget can modify the details which are visible on the other widget.
I am looking for a solution to this issue, if there's any other way to achieve the goal except finding which widget is on the screen then please let me know that as well.
My final goal is to allow the long process to be executed from only the visible widget(s).
Please run the app once, to understand the following details properly.
Note 2:
I have tried to use mounted property of the state to determine if it can be used or not but it shows true for both widgets (MainPage TextDisplay and NewPage TextDisplay)
Let me know in the comments if more details or I missed something which is required.
Use the following sample code with provider dependency included for reproducing the problem:
// add in pubspec.yaml: provider: ^4.3.2+1
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
print('MainPage: build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'MainPage TextDisplay',
),
SizedBox(
height: 20,
),
RaisedButton(
child: Text('Open New Page'),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NewPage(),
)),
),
],
),
),
);
}
}
class TextDisplay extends StatefulWidget {
final String name;
const TextDisplay({Key key, #required this.name}) : super(key: key);
#override
_TextDisplayState createState() => _TextDisplayState();
}
class _TextDisplayState extends State<TextDisplay> {
#override
Widget build(BuildContext context) {
return Container(
child: ChangeNotifierProvider.value(
value: dataHolder,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(child: Text(widget.name)),
SizedBox(
height: 20,
),
Consumer<DataHolder>(
builder: (context, holder, child) {
// need to detect if this widget is on the screen,
// only then we should go ahead with this long process
// otherwise we should skip this long process
doLongProcess(widget.name);
return Text(holder.data);
},
),
RaisedButton(
child: Text('Randomize'),
onPressed: () => randomizeData(),
),
],
),
),
);
}
void doLongProcess(String name) {
print('$name: '
'Doing a long process using the new data, isMounted: $mounted');
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('NewPage: build');
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: Text('New Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'NewPage TextDisplay',
),
],
),
),
);
}
}
/////////////////// Data Holder Class and methods ///////////////////
class DataHolder extends ChangeNotifier {
String _data;
String get data => _data ?? 'Nothing to show, Yet!';
setData(String newData) {
print('\n new data found: $newData');
_data = newData;
notifyListeners();
}
}
final dataHolder = DataHolder();
randomizeData() {
int mills = DateTime.now().millisecondsSinceEpoch;
dataHolder.setData(mills.toString());
}
Posting solution for others to refer.
Refer to this flutter plugin/package:
https://pub.dev/packages/visibility_detector
The solution code:
// add in pubspec.yaml: provider: ^4.3.2+1
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:visibility_detector/visibility_detector.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
print('MainPage: build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'MainPage TextDisplay',
),
SizedBox(
height: 20,
),
RaisedButton(
child: Text('Open New Page'),
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
builder: (context) => NewPage(),
)),
),
],
),
),
);
}
}
class TextDisplay extends StatefulWidget {
final String name;
const TextDisplay({Key key, #required this.name}) : super(key: key);
#override
_TextDisplayState createState() => _TextDisplayState();
}
class _TextDisplayState extends State<TextDisplay> {
/// this holds the latest known status of the widget's visibility
/// if [true] then the widget is fully visible, otherwise it is false.
///
/// Note: it is also [false] if the widget is partially visible since we are
/// only checking if the widget is fully visible or not
bool _isVisible = true;
#override
Widget build(BuildContext context) {
return Container(
child: ChangeNotifierProvider.value(
value: dataHolder,
/// This is the widget which identifies if the widget is visible or not
/// To my suprise this is an external plugin which is developed by Google devs
/// for the exact same purpose
child: VisibilityDetector(
key: ValueKey<String>(widget.name),
onVisibilityChanged: (info) {
// print('\n ------> Visibility info:'
// '\n name: ${widget.name}'
// '\n visibleBounds: ${info.visibleBounds}'
// '\n visibleFraction: ${info.visibleFraction}'
// '\n size: ${info.size}');
/// We use this fraction value to determine if the TextDisplay widget is
/// fully visible or not
/// range for fractional value is: 0 <= visibleFraction <= 1
///
/// Meaning we can also use fractional values like, 0.25, 0.3 or 0.5 to
/// find if the widget is 25%, 30% or 50% visible on screen
_isVisible = info.visibleFraction == 1;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(child: Text(widget.name)),
SizedBox(
height: 20,
),
Consumer<DataHolder>(
builder: (context, holder, child) {
/// now that we have the status of the widget's visiblity
/// we can skip the long process when the widget is not visible.
if (_isVisible) {
doLongProcess(widget.name);
}
return Text(holder.data);
},
),
RaisedButton(
child: Text('Randomize'),
onPressed: () => randomizeData(),
),
],
),
),
),
);
}
void doLongProcess(String name) {
print('\n ============================ \n');
print('$name: '
'Doing a long process using the new data, isMounted: $mounted');
final element = widget.createElement();
print('\n name: ${widget.name}'
'\n element: $element'
'\n owner: ${element.state.context.owner}');
print('\n ============================ \n');
}
}
class NewPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('NewPage: build');
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: Text('New Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextDisplay(
name: 'NewPage TextDisplay',
),
],
),
),
);
}
}
/////////////////// Data Holder Class and methods ///////////////////
class DataHolder extends ChangeNotifier {
String _data;
String get data => _data ?? 'Nothing to show, Yet!';
setData(String newData) {
print('\n new data found: $newData');
_data = newData;
notifyListeners();
}
}
final dataHolder = DataHolder();
randomizeData() {
int mills = DateTime.now().millisecondsSinceEpoch;
dataHolder.setData(mills.toString());
}

How to copy the value of a Widget Text to another using onPressed property of a button

I need to copy a value of a Text Widget and copy this to another.
I tried to this using keys, but I don't know how to acess the Text Widget in this case.
Is it possible to do in Flutter, using the onPressed property?
import 'package:flutter/material.dart';
class TextWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Value to be copied",
key: Key('text1')
),
RaisedButton(
onPressed: (){
// code here
},
child: Text("Copy value"),
),
SizedBox(height: 40),
Text(
"",
key: Key('text2')
)
],
),
),
);
}
}
Answering your question directly: you can access text inside Text widget using its data property.
Text widget = Text('text value');
String text = widget.data;
print(text); // text value
Next, you can't access widgets by their key properties. At least you shouldn't, because they were designed for different purpose: here's a video and an article about keys in Flutter.
What you can do here is turn your TextWidget from StatelessWidget into StatefulWidget and render contents of your second Text based on the state. Good introduction into what the state is and why you should use it can be found on official Flutter website: Start thinking declaratively.
Then you can save your first Text widget in a variable and then access its contents directly using data property update, then update state of the whole widget.
Example 1 on DartPad
More canonical and in general preferrable approach is to render contents of both buttons based on the state and get desired text from state variable and not from the widget itself, as proposed by Sebastian and MSARKrish.
Example 2 on DartPad
Note that you can't change data attribute of a Text widget imperatively, like you would do in JavaScript DOM API with innerText:
_textWidget.data = "New text"; // Doesn't work
because its data is final. In Flutter you have to think declaratively, and it worth it.
Try this
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String _text = "Value to be copied";
bool _buttonToggle;
#override
void initState() {
super.initState();
_buttonToggle = false;
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_text),
SizedBox(height: 40),
RaisedButton(
onPressed: _toggle,
child: Text("Copy value"),
),
Switch(
value: _buttonToggle,
onChanged: (_) => _toggle(),
),
SizedBox(height: 40),
Text(_buttonToggle ? _text : '')
],
);
}
void _toggle() {
setState(() => _buttonToggle = !_buttonToggle);
}
}
class TextWidget extends StatefulWidget {
#override
_TextWidgetState createState() => _TextWidgetState();
}
class _TextWidgetState extends State<TextWidget> {
String text1Value = "text to be copied";
String text2Value = "";
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
text1Value,
),
RaisedButton(
onPressed: () {
setState(() {
text2Value = text1Value;
});
},
child: Text("Copy value"),
),
SizedBox(height: 40),
Text(
text2Value,
)
],
),
),
);
}
}

How to properly reuse a Provider in Flutter

So I have this ChangeNotifierProvider high in my widget tree as I am seeing many children widgets to listen to its value.
What I am currently doing is that I pass down the Provider.of(context) object from the parent widget into it's children via constructors whenever I am to reuse some values/functions on my children widgets. For example, everytime I create a Provider.of(context) object for my children widgets, it seems that it does not carry over the updated values I have on the Parent Provider but rather this one has my default null/0/'empty' ones like it has only been created. This lead me to pass down the initial Provider.of(context) object to each children that will use the updated values and functions of the ChangeNotifier.
This setup is working for me, however, when my Widget Tree has started being complex, I am constantly passing down values through each widget and to some that do not even use it at all just for its children to listen to the main provider.
I think what I may be doing now is anti-pattern of the Provider Architecture, I am hoping you guys can help me on a more optimized and efficient way of doing this.
Thank you very much!
P.S. There are some things in the documentation that I am not yet quite grasping properly.
Edits Below to include sample code and visualization:
provider_type.dart
class ProviderType extends ChangeNotifier{
String valueA = '';
String valueB = '';
}
home.dart
import ..provider_type.dart
...
Widget build(BuildContext context){
return ChangeNotifierProvider<ProviderType>(
create: (context) => ProviderType(),
child: ScreenColumn();
);
}
...
screen_column.dart
import ..screen_a.dart
import ..screen_b.dart
class ScreenColumn extends StatelessWidget{
Widget build(BuildContext context){
var providerType = Provider.of<ProviderType>(context);
return Column(
children: <Widget>[
ScreenA(providerType: providerType),
ScreenB(providerType: providerType),
],
);
}
}
screen_a.dart
class ScreenA extends StatelessWidget{
final ProviderType providerType;
ScreenA({this.providerType});
Widget build(BuildContext context){
return Text(
'${providerType.valueA}'
);
}
}
screen_b.dart
import ..screen_c.dart
class ScreenB extends StatelessWidget{
final ProviderType providerType;
ScreenB({this.providerType});
Widget build(BuildContext context){
return ScreenC(providerType: providerType);
}
}
screen_c.dart
class ScreenC extends StatelessWidget{
final ProviderType providerType;
ScreenB({this.providerType});
Widget build(BuildContext context){
return Column(
children: <Widget>[
Text(
'${providerType.valueA}'
)
Text(
'${providerType.valueB}'
)
Text(
'${providerType.valueC}'
)
]
);
}
}
Visualization
So what I am currently doing is to pass down the object providerType from ScreenColumn to Screens A, B, and C just so each of them have the same "Source of Values". Cause when I try to make different Provider.of objects and use them, they do not share the same updated values when I do some computation.
Is there something I can do to make this more efficient or is there a better way that I need to do?
To those who may be wondering or are searching for answers to the same question, look at my sample code below that shows how you can reuse/share your Provider Values and Functions at any point in your widget tree as long as they are under your Parent Provider.
And yes, you can actually just create Provider.of Objects anywhere in
your tree without passing down the initial Provider.of object that you
have created.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ProviderType extends ChangeNotifier {
String value = DateTime.now().toString();
changeValue() {
value = DateTime.now().toString();
notifyListeners();
}
}
void main() => runApp(AppIndex());
class AppIndex extends StatelessWidget {
const AppIndex({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ProviderType>(
create: (context) => ProviderType(),
child: MaterialApp(
home: Home(),
),
);
}
}
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Scaffold(
appBar: AppBar(
title: Text('Sample App'),
),
body: ScreenColumn(),
floatingActionButton: FloatingActionButton.extended(
onPressed: () => providerType.changeValue(),
label: Text('ChangeValue'),
),
);
}
}
class ScreenColumn extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
ScreenA(),
ScreenB(),
ScreenC(),
ScreenC(),
],
));
}
}
class ScreenA extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.red,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(providerType.value),
),
);
}
}
class ScreenB extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.blue,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(providerType.value),
ScreenC(),
ScreenC(),
],
),
),
);
}
}
class ScreenC extends StatelessWidget {
#override
Widget build(BuildContext context) {
// var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.green,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text('This is Screen B with no Provider.of Object'),
ScreenD(),
ScreenD(),
ScreenD(),
],
),
),
);
}
}
class ScreenD extends StatelessWidget {
#override
Widget build(BuildContext context) {
var providerType = Provider.of<ProviderType>(context);
return Card(
color: Colors.yellow,
elevation: 8.0,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'This is Screen D. A Provider.of object was created here without inheriting the Parent\'s Provider.of object.'),
Text(providerType.value),
],
),
),
);
}
}