I am having a problem where when I try to use a switch widget it will not work properly inside of an alert box as in it does not switch over to the second state it just bounces whenever I try to flick it. I am wondering if this is because there is a problem with the switch itself or how I displayed it in the box? Thanks!
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
home: SwitchDemo(),
);
}
}
class SwitchDemo extends StatefulWidget {
const SwitchDemo({Key key}) : super(key: key);
#override
State<StatefulWidget> createState() => new _TabsPageState();
}
class _TabsPageState extends State<SwitchDemo> {
bool isInstructionView;
#override
void initState() {
super.initState();
isInstructionView = Global.shared.isInstructionView;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("add data"),
),
body: Container(
child: TextButton(
child: Text('Open Alert Box'),
onPressed: () => {
showDialog(
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 20,
vertical:
MediaQuery.of(context).size.height / 20,
),
child: AlertDialog(
content: Container(
child: Switch(
value: isInstructionView,
onChanged: (bool isOn) {
if (isInstructionView == false) {
} else if (isInstructionView == true) {}
setState(() {
isInstructionView = isOn;
Global.shared.isInstructionView = isOn;
isOn = !isOn;
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
),
),
),
);
})
}),
));
}
}
class Global {
static final shared = Global();
bool isInstructionView = false;
}
Wrap you AlertDialog with StatefulBuilder.
here is full code:
import 'package:flutter/material.dart';
class SwitchDemo extends StatefulWidget {
const SwitchDemo({Key? key}) : super(key: key);
#override
State<StatefulWidget> createState() => new _TabsPageState();
}
class _TabsPageState extends State<SwitchDemo> {
late bool isInstructionView;
#override
void initState() {
super.initState();
isInstructionView = Global.shared.isInstructionView;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("add data"),
),
body: Container(
child: TextButton(
child: Text('Open Alert Box'),
onPressed: () => {
showDialog(
context: context,
builder: (BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(
horizontal: MediaQuery.of(context).size.width / 20,
vertical: MediaQuery.of(context).size.height / 20,
),
child: StatefulBuilder(builder: (context, setState) {
return AlertDialog(
content: Container(
child: Switch(
value: isInstructionView,
onChanged: (bool isOn) {
print(isInstructionView);
setState(() {
isInstructionView = !isInstructionView;
});
},
activeColor: Colors.blue,
inactiveTrackColor: Colors.grey,
inactiveThumbColor: Colors.grey,
),
),
);
}),
);
},
)
}),
));
}
}
class Global {
static final shared = Global();
bool isInstructionView = false;
}
Does it answer your question?
ref: https://stackoverflow.com/a/57240941/10157127
Related
I am trying to use InheritedWidget approach to share state down the Widget tree. For this, I am making a simple counter app. You can add, subtract, multiply or divide the counter however you like.
It's a small demo so best practices are not followed. The line with code context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>() seem to be null for some reason. When looking at samples and doc, it should find the MyInheritedWidget in the widget tree and return it. However, I am getting complaints from flutter tool that it is null. And, in deed it is null when asserted as well.
What is the reasoning here for failed return here? And, how do I need to do it such that I can receive the instance?
File: main.dart
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Counter(),
);
}
}
class Counter extends StatefulWidget {
const Counter({Key? key}) : super(key: key);
#override
CounterState createState() => CounterState();
}
class CounterState extends State<Counter> {
int counter = 0;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
centerTitle: true,
),
body: MyInheritedWidget(
counterState: this,
child: Builder(
builder: (BuildContext innerContext) {
return CounterViewer(
counterState: MyInheritedWidget.of(context).counterState);
},
),
),
);
}
void addCounter(int value) {
setState(() {
counter++;
});
}
void subtractCounter(int value) {
setState(() {
counter--;
});
}
void multiplyCounter(int value) {
setState(() {
counter *= value;
});
}
void divideCounter(int value) {
setState(() {
counter = (counter / value).toInt();
});
}
}
class MyInheritedWidget extends InheritedWidget {
final CounterState counterState;
const MyInheritedWidget(
{Key? key, required Widget child, required this.counterState})
: super(key: key, child: child);
static MyInheritedWidget of(BuildContext context) {
final MyInheritedWidget? widget =
context.dependOnInheritedWidgetOfExactType<MyInheritedWidget>();
assert(widget != null);
return widget!;
}
#override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return true;
}
}
class CounterViewer extends StatelessWidget {
final CounterState counterState;
const CounterViewer({Key? key, required this.counterState}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: Colors.green.shade200,
width: MediaQuery.of(context).size.width,
height: 180,
child: Center(
child: Text(
'220',
style: TextStyle(
color: Colors.grey.shade50,
fontSize: 60,
fontWeight: FontWeight.bold,
),
),
),
),
Container(
color: Colors.grey.shade300,
padding: EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {},
child: Text('Add'),
),
ElevatedButton(
onPressed: () {},
child: Text('Subtract'),
),
ElevatedButton(
onPressed: () {},
child: Text('Multiply'),
),
ElevatedButton(
onPressed: () {},
child: Text('Divide'),
),
],
),
)
],
);
}
}
Update: I seem to have passed the wrong context to the dependOnInheritedWidgetOfExactType() method. Changing from context to innerContext fixed the issue.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Counter App'),
centerTitle: true,
),
body: MyInheritedWidget(
counterState: this,
child: Builder(
builder: (BuildContext innerContext) {
return CounterViewer(
counterState: MyInheritedWidget.of(innerContext).counterState);
},
),
),
);
}
I'm new to flutter and I'm trying to get the countdown-variable from the SetTimerPage to the CupertinoTimePickerButton-Page. Then change it there to the chosen time and give it back. Next I want to give the changed countdown to the next page where a countdown animation starts. I'm also open for suggestions to improve my syntax. Thank you :)
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.red,
),
home: SetTimerPage(),
);
}
}
class SetTimerPage extends StatefulWidget {
#override
_SetTimerPageState createState() => _SetTimerPageState();
}
class _SetTimerPageState extends State<SetTimerPage> {
final Duration countdown = Duration(minutes: 0, seconds: 0);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('RoundONE'),
),
body: Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CustomRowWidget(
icon: Icons.alarm_sharp,
text: 'COUNTDOWN',
button: CupertinoTimePickerButton(passedValue: countdown),
),
StartButton(countdown: countdown),
],
),
),
);
}
}
class CupertinoTimePickerButton extends StatefulWidget {
Duration passedValue;
CupertinoTimePickerButton({
Key key,
#required this.passedValue,
}) : super(key: key);
#override
_CupertinoTimePickerButtonState createState() =>
_CupertinoTimePickerButtonState();
}
class _CupertinoTimePickerButtonState extends State<CupertinoTimePickerButton> {
#override
Widget build(BuildContext context) {
return MaterialButton(
child: Text(
_formatDuration(widget.passedValue),
style: TextStyle(color: Colors.white),
),
color: Colors.redAccent,
onPressed: () {
_cupertinoTimeSetter(context);
},
);
}
Future _cupertinoTimeSetter(BuildContext context) {
return showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return Container(
height: MediaQuery.of(context).copyWith().size.height / 2,
child: CupertinoTimerPicker(
onTimerDurationChanged: (Duration newDuration) {
setState(() {
widget.passedValue = newDuration;
});
},
minuteInterval: 1,
secondInterval: 5,
mode: CupertinoTimerPickerMode.ms,
),
);
},
);
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "$twoDigitMinutes:$twoDigitSeconds";
}
}
The best way is to use a call back that will give back the value to it's parent and for this case you can use ValueChanged. Then after the parent will be incharged to change the variable value.
class SetTimerPage extends StatefulWidget {
#override
_SetTimerPageState createState() => _SetTimerPageState();
}
class _SetTimerPageState extends State<SetTimerPage> {
Duration countdown;
#override
void initState() {
super.initState();
countdown = Duration(minutes: 0, seconds: 0);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('RoundONE'),
),
body: Container(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CustomRowWidget(
icon: Icons.alarm_sharp,
text: 'COUNTDOWN',
button: CupertinoTimePickerButton(
passedValue: countdown,
onChanged: (Duration duration) {
setState(() {
this.countdown = duration;
});
},
),
),
StartButton(countdown: countdown),
],
),
),
);
}
}
class CupertinoTimePickerButton extends StatelessWidget {
final Duration passedValue;
final ValueChanged<Duration> onChanged;
#override
Widget build(BuildContext context) {
return MaterialButton(
child: Text(
_formatDuration(widget.passedValue),
style: TextStyle(color: Colors.white),
),
color: Colors.redAccent,
onPressed: () {
_cupertinoTimeSetter(context);
},
);
}
Future _cupertinoTimeSetter(BuildContext context) {
return showModalBottomSheet(
context: context,
builder: (BuildContext builder) {
return Container(
height: MediaQuery.of(context).copyWith().size.height / 2,
child: CupertinoTimerPicker(
onTimerDurationChanged: this.onChanged,
minuteInterval: 1,
secondInterval: 5,
mode: CupertinoTimerPickerMode.ms,
),
);
},
);
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "$twoDigitMinutes:$twoDigitSeconds";
}
}
When calling MediaQuery.of(context).padding.top in the parent widget (ProductsOverviewScreen) the value returned is as expected - 24. But when calling the same property from a nested widget (ProductsGrid) of the parent that we are talking about the value is always 0. Is this normal behavior?
products_overview_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../widgets/app_drawer.dart';
import '../screens/cart_screen.dart';
import '../widgets/products_grid.dart';
import '../widgets/badge.dart';
import '../providers/cart.dart';
import '../providers/products_provider.dart';
enum FilterOptions {
showAll,
onlyFavorites,
}
class ProductsOverviewScreen extends StatefulWidget {
#override
_ProductsOverviewScreenState createState() => _ProductsOverviewScreenState();
}
class _ProductsOverviewScreenState extends State<ProductsOverviewScreen> {
bool _showFavorites = false;
bool _isLoading = false;
bool _isInit = false;
#override
void initState() {
// Future.delayed(Duration(seconds: 1), () {
// Provider.of<Products>(context).fetchAndSetProducts();
// });
super.initState();
}
#override
void didChangeDependencies() {
if (!_isInit) {
setState(() {
_isLoading = true;
});
Provider.of<Products>(context).fetchAndSetProducts().then((_) {
setState(() {
_isLoading = false;
});
});
}
_isInit = true;
super.didChangeDependencies();
}
Future<void> refreshProducts(BuildContext context) async {
await Provider.of<Products>(context, listen: false).fetchAndSetProducts();
}
#override
Widget build(BuildContext context) {
final scaffoldKey = GlobalKey();
final appBar = AppBar(
title: Text('My Shop'),
actions: <Widget>[
Consumer<Cart>(
builder: (ctx, cart, child) => Badge(
child: child,
value: cart.length.toString(),
),
child: IconButton(
onPressed: () {
final scaffoldState = scaffoldKey.currentState as ScaffoldState;
scaffoldState.hideCurrentSnackBar();
Navigator.of(context).pushNamed(CartScreen.routeName);
},
icon: Icon(
Icons.shopping_cart,
color: Theme.of(context).accentColor,
),
),
),
PopupMenuButton(
onSelected: (FilterOptions selectedValue) {
setState(() {
if (selectedValue == FilterOptions.onlyFavorites) {
_showFavorites = true;
} else {
_showFavorites = false;
}
});
},
icon: Icon(Icons.more_vert),
itemBuilder: (_) => [
PopupMenuItem(
child: Text('Only Favorites'),
value: FilterOptions.onlyFavorites),
PopupMenuItem(
child: Text('Show All'),
value: FilterOptions.showAll,
),
],
),
],
);
return Scaffold(
key: scaffoldKey,
appBar: appBar,
drawer: AppDrawer(),
body: _isLoading
? Center(child: CircularProgressIndicator())
: RefreshIndicator(
onRefresh: () => refreshProducts(context),
child: ProductsGrid(_showFavorites, appBar.preferredSize.height),
),
);
}
}
//56
products_grid.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/products_provider.dart';
import '../widgets/product_item.dart';
class ProductsGrid extends StatelessWidget {
final bool _showFavorites;
final double _appBarHeight;
ProductsGrid(this._showFavorites, this._appBarHeight);
#override
Widget build(BuildContext context) {
final productsData = Provider.of<Products>(context);
final products =
_showFavorites ? productsData.favoriteItems : productsData.items;
final mediaQuery = MediaQuery.of(context);
return productsData.items.isEmpty
? SingleChildScrollView(
physics: AlwaysScrollableScrollPhysics(),
child: Container(
width: mediaQuery.size.width,
height: mediaQuery.size.height -
mediaQuery.padding.top -
_appBarHeight,
// child: Center(
// child: Text(
// 'There are no products.',
// style: TextStyle(
// color: Colors.grey,
// fontSize: 16,
// ),
// ),
// ),
),
)
: GridView.builder(
padding: const EdgeInsets.all(15),
itemCount: products.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemBuilder: (ctx, index) => ChangeNotifierProvider.value(
value: products[index],
child: ProductItem(),
),
);
}
}
You can copy paste run full code below
You can use MediaQueryData.fromWindow(window).padding.top
code snippet
import 'dart:ui';
...
final statusbarHeight2 = MediaQueryData.fromWindow(window).padding.top;
working demo
full code
import 'package:flutter/material.dart';
import 'dart:ui';
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: ProductsOverviewScreen(title: 'Flutter Demo Home Page'),
);
}
}
class ProductsOverviewScreen extends StatefulWidget {
ProductsOverviewScreen({Key key, this.title}) : super(key: key);
final String title;
#override
_ProductsOverviewScreenState createState() => _ProductsOverviewScreenState();
}
class _ProductsOverviewScreenState extends State<ProductsOverviewScreen> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
final statusbarHeight1 = MediaQueryData.fromWindow(window).padding.top;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ProductsGrid(),
Text(
'$statusbarHeight1',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class ProductsGrid extends StatelessWidget {
const ProductsGrid({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final statusbarHeight2 = MediaQueryData.fromWindow(window).padding.top;
return Text(
'$statusbarHeight2',
);
}
}
You can get all information about window using window object that provided by dart:ui. Here an example of finding exact size of status bar;
Firstly add this top of the dart file:
import 'dart:ui';
And use window object to find height of the status bar:
final statusBarHeight = window.padding.top / window.devicePixelRatio;
I have a two-page app. On-Page One I am showing an UUID which changes every 1 second. It is shown using listview. Once the user clicks on the list view it goes to the second page and shows the data on that card.
It should have been the changing UUID. but the data shown is static UUID. How I can pass the data changed on page 1 to page 2?
import 'dart:async';
import 'package:uuid/uuid.dart';
import 'package:uuid/uuid_util.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
List<EuropeanCountries> europeanCountries = [];
class EuropeanCountries {
String myText;
String myUuid;
EuropeanCountries({
this.myText,
this.myUuid,
});
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
int _perPage = 50;
ScrollController _myScrollController = ScrollController();
void _incrementCounter() async {
const ThreeSec = const Duration(seconds: 1);
this._counter++;
europeanCountries.insert(
0,
EuropeanCountries(
myText: this._counter.toString(),
));
print(europeanCountries[0].myText);
setState(() {});
}
void getMoreData() {
print('adding More Product ');
europeanCountries.add(EuropeanCountries(
myText: this._counter.toString(),
));
//europeanCountries.insert(0, EuropeanCountries(myText:this._counter.toString(), myButtonText: "", myColor: Colors.blue));
setState(() {});
}
void generateUUID() async {
var uuid = Uuid();
for (int i = 0; i < 6000; i++) {
await new Future.delayed(new Duration(milliseconds: 1000));
for (EuropeanCountries currCountry in europeanCountries) {
currCountry.myUuid = uuid.v1();
}
setState(() {});
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
generateUUID();
_myScrollController.addListener(() {
double maxscroll = _myScrollController.position.maxScrollExtent;
double currentScroll = _myScrollController.position.pixels;
double delta = MediaQuery.of(context).size.height * 0.25;
print("mac Scroll Controller - " + maxscroll.toString());
print("Current Scroll Controller - " + currentScroll.toString());
print("delta Scroll Controller - " + delta.toString());
if ((maxscroll - currentScroll) < delta) {
getMoreData();
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: _myListView(context),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _myListView(BuildContext context) {
// backing data
return Container(
child: europeanCountries.length == 0
? Center(
child: Text('No Product to Display'),
)
: ListView.builder(
controller: _myScrollController,
itemCount: europeanCountries.length,
reverse: false,
itemBuilder: (context, index) {
return myContainer(index: index);
},
),
);
}
}
class myContainer extends StatefulWidget {
final int index;
const myContainer({Key key, this.index}) : super(key: key);
#override
_myContainerState createState() => _myContainerState();
}
class _myContainerState extends State<myContainer> {
#override
Widget build(BuildContext context) {
return Container(
height: 120,
decoration: BoxDecoration(
border: Border.all(color: Colors.blue[700]),
shape: BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(8)),
),
margin: EdgeInsets.all(20),
child: Column(
children: <Widget>[
Text(europeanCountries[widget.index].myText),
SizedBox(
height: 15,
),
RaisedButton(
child: Text('Detail'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondRoute(
myCountry: europeanCountries[widget.index],
)),
);
},
color: Colors.blue[700],
textColor: Colors.white,
padding: EdgeInsets.fromLTRB(10, 10, 10, 10),
splashColor: Colors.black,
),
Text(europeanCountries[widget.index].myUuid != null
? europeanCountries[widget.index].myUuid
: 'Default')
],
),
);
}
}
class SecondRoute extends StatefulWidget {
final EuropeanCountries myCountry;
const SecondRoute({Key key, this.myCountry}) : super(key: key);
#override
_SecondRouteState createState() => _SecondRouteState();
}
class _SecondRouteState extends State<SecondRoute> {
#override
void didUpdateWidget(SecondRoute oldWidget) {
// TODO: implement didUpdateWidget
super.didUpdateWidget(oldWidget);
setState(() {});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
Navigator.pop(context);
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Text(widget.myCountry.myUuid != null
? widget.myCountry.myText
: 'default'),
),
SizedBox(height: 15),
Center(
child: Text(widget.myCountry.myUuid != null
? widget.myCountry.myUuid
: 'default'),
),
],
),
),
),
);
}
}
https://imgur.com/VqRfcZY
<blockquote class="imgur-embed-pub" lang="en" data-id="VqRfcZY"></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>
I'm trying to get the following to work. Changes to the app model state are not picked up via the InheritedWidget 'AppStateProvider'. I've manage to get this working with sinks/streams but was hoping to established a simpler structure.
This is just a test application to switch between various app modes.
What's missing?
import 'package:flutter/material.dart';
void main() {
runApp(AppStateProvider(
child: RootPage(),
appState: new AppState(),
));
}
enum AppMode { introduction, login, home }
class AppState {
AppMode appMode;
AppState({
this.appMode = AppMode.introduction,
});
}
class AppStateProvider extends InheritedWidget {
final AppState appState;
AppStateProvider({Key key, Widget child, this.appState})
: super(key: key, child: child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static AppStateProvider of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(AppStateProvider)
as AppStateProvider);
}
}
class RootPage extends StatelessWidget {
AppMode _mode;
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Inherited Widget Test',
theme: new ThemeData(
primarySwatch: Colors.blueGrey,
),
home: _body(context),
);
}
Widget _body(BuildContext context) {
final provider = AppStateProvider.of(context); //Registers as a listener
final state = provider.appState;
_mode = state.appMode;
return new Stack(
children: <Widget>[
new Offstage(
offstage: _mode != AppMode.introduction,
child: new MaterialApp(
home: ColorsListPage(
color: Colors.red,
targetAppMode: AppMode.login,
title: "Intro",
),
),
),
new Offstage(
offstage: _mode != AppMode.login,
child: new MaterialApp(
home: ColorsListPage(
color: Colors.blue,
targetAppMode: AppMode.home,
title: "Login",
),
),
),
new Offstage(
offstage: _mode != AppMode.home,
child: new MaterialApp(
home: ColorsListPage(
color: Colors.green,
targetAppMode: AppMode.introduction,
title: "Home",
),
),
),
],
);
}
}
class ColorDetailPage extends StatefulWidget {
final String title;
final MaterialColor color;
final int materialIndex;
final AppMode targetAppMode;
ColorDetailPage(
{this.color, this.title, this.targetAppMode, this.materialIndex: 500});
#override
_ColorDetailPageState createState() => new _ColorDetailPageState();
}
class _ColorDetailPageState extends State<ColorDetailPage> {
#override
Widget build(BuildContext context) {
final provider = AppStateProvider.of(context);
return Scaffold(
appBar: AppBar(
backgroundColor: widget.color,
title: Text(
'$widget.title[$widget.materialIndex]',
),
),
body: Container(
color: widget.color[widget.materialIndex],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
provider.appState.appMode = widget.targetAppMode;
});
},
heroTag: null,
),
);
}
}
class ColorsListPage extends StatefulWidget {
final MaterialColor color;
final String title;
final ValueChanged<int> onPush;
final AppMode targetAppMode;
final List<int> materialIndices = [
100,
200,
300,
400,
500,
600,
700,
800,
900,
];
ColorsListPage({this.color, this.targetAppMode, this.title, this.onPush});
#override
_ColorsListPageState createState() => new _ColorsListPageState();
}
class _ColorsListPageState extends State<ColorsListPage> {
#override
Widget build(BuildContext context) {
final provider = AppStateProvider.of(context);
return new Scaffold(
appBar: AppBar(
title: Text(widget.title),
backgroundColor: widget.color,
),
body: Container(
color: Colors.white,
child: _buildList(context),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
provider.appState.appMode = widget.targetAppMode;
});
},
heroTag: null,
));
}
Widget _buildList(BuildContext context) {
return ListView.builder(
itemCount: widget.materialIndices.length,
itemBuilder: (BuildContext content, int index) {
int materialIndex = widget.materialIndices[index];
return Container(
color: widget.color[materialIndex],
child: ListTile(
title: Text(
"$materialIndex",
style: TextStyle(fontSize: 24.0),
),
trailing: Icon(Icons.chevron_right),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ColorDetailPage(
color: widget.color,
title: widget.title,
targetAppMode: widget.targetAppMode,
materialIndex: materialIndex,
)),
);
}
//onTap: () => onPush(materialIndex),
));
},
);
}
}
You need to wrap your InheritedWidget inside a StatefulWidget
class _AppStateProvider extends InheritedWidget {
final AppStateProviderState data;
_AppStateProvider({Key key, #required Widget child, #required this.data})
: super(key: key, child: child);
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
}
class AppStateProvider extends StatefulWidget {
final Widget child;
final AppState appState;
AppStateProvider({
#required this.child,
#required this.appState,
});
static AppStateProviderState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(_AppStateProvider)
as _AppStateProvider)
.data;
}
#override
AppStateProviderState createState() => AppStateProviderState(
appState,
);
}
class AppStateProviderState extends State<AppStateProvider> {
AppState appState;
AppStateProviderState(this.appState);
void updateAppMode(AppMode appMode) {
setState(() {
appState.appMode = appMode;
});
}
#override
Widget build(BuildContext context) {
return _AppStateProvider(
data: this,
child: widget.child,
);
}
}
for more information
pay attention to this method:
void updateAppMode(AppMode appMode) {
setState(() {
appState.appMode = appMode;
});
}
you can use it like this:
floatingActionButton: FloatingActionButton(
onPressed: () {
provider.updateAppMode(widget.targetAppMode);
},
heroTag: null,
),