I am trying to display some widgets like a CheckBox or a Switch in an OverlayEntry. The overlay is built in a tap event.
The problem is that the bool _value is updated only the first time I tap the CheckBox inside the overlay, but that CheckBox doesn't update its State. The strange thing is that (only the first tap) the tap updates the CheckBox outside the overlay instead of the one inside it.
What I have missed here?
Below a full snippet to reproduce this.
Thanks for your time!
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _value = true;
_buildOverlay() {
OverlayEntry _overlayEntry;
OverlayState _overlayState = Overlay.of(context);
_overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return Stack(
children: <Widget>[
Material(
child: Container(
padding: EdgeInsets.all(100),
color: Colors.lightBlue,
child: Checkbox(
value: _value,
onChanged: (bool value) { print("$value $_value"); setState(() => _value = value); },
),
),
),
],
);
},
);
_overlayState.insert(_overlayEntry);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(""),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
FloatingActionButton(
onPressed: () {_buildOverlay();},
child: Icon(Icons.add),
),
Checkbox(
value: _value,
onChanged: (bool value) { print("$value $_value"); setState(() => _value = value); },
),
],
),
],
),
),
);
}
}
UPDATE:
In addition to the anmol.majhail solution, using ValueListenableBuilder could be another solution without the need to do a StatefulWidget.
The declaration of _value becomes:
var _value = ValueNotifier<bool>(false);
and here the _overlayEntry in the _buildOverlay() function:
_overlayEntry = OverlayEntry(
builder: (BuildContext context) {
return Material(
child: Container(
padding: EdgeInsets.all(100),
color: Colors.lightBlue,
child: ValueListenableBuilder<bool>(
valueListenable: _value,
builder: (context, value, child) {
return Checkbox(
value: _value.value,
onChanged: (bool value) {
print("$value $_value");
setState(() => _value.value = value);
},
);
},
),
),
);
},
);
Issue here is Overlay entry have different context. In Order to make it work you need to separate the overlay entry in a separate stateful widget - through which you can manage the checkbox state.
working Code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _value = true;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(""),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
FloatingActionButton(
onPressed: () {
OverlayState _overlayState = Overlay.of(context);
_overlayState.insert(OverlayEntry(
builder: (BuildContext context) {
return OverlayClass(
val: _value,
);
},
));
// _buildOverlay(context);
},
child: Icon(Icons.add),
),
Checkbox(
value: _value,
onChanged: (bool value) {
print("$value $_value");
setState(() => _value = value);
},
),
],
),
],
),
),
);
}
}
class OverlayClass extends StatefulWidget {
final bool val;
OverlayClass({this.val});
#override
_OverlayClassState createState() => _OverlayClassState();
}
class _OverlayClassState extends State<OverlayClass> {
bool _value;
#override
void initState() {
super.initState();
_value = widget.val;
}
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Material(
child: Container(
padding: EdgeInsets.all(100),
color: Colors.lightBlue,
child: Checkbox(
value: _value,
onChanged: (bool value) {
print("$value $_value");
setState(() => _value = value);
},
),
),
),
],
);
}
}
Related
I have a Bottom Navigation in parent widget, and a few textfields in child widget. When user clicks on the navigation tab and if one of the textfields is empty, it will set focus on the particular textfields.
I am using the constructor method learnt from one of the developer however I couldn't get it work. It seems like I didn't pass over the context properly. I am not sure.
Anyone able to spot my mistakes or advise other methods which can achieve the same result?
login.dart
class Login extends StatefulWidget{
#override
State<StatefulWidget> createState() {
return _LoginState();
}
}
class _LoginState extends State<Login> {
FocusNode focusNode;
Page1 focus;
#override
void initState() {
super.initState();
focusNode = new FocusNode();
focus = new Page1(focusNode: focusNode);
}
int currentBottomNavIndex = 0;
List<Widget> bottomNav = [
Page1(),
Page2(),
];
onTapped(int index) {
//if(textfield not empty) {
//setState(() {
//currentBottomNavIndex = index;
//});
//}else {
focus.setFocus(context);
//}
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: Text('Login Page'),
),
body: bottomNav[currentBottomNavIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: onTapped,
//onTap: requestFocus(context),
currentIndex: currentBottomNavIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Page1"),
),
BottomNavigationBarItem(
icon: Icon(Icons.mail),
title: Text('Page2'),
),
],
),
);
}
}
page1.dart
class Page1 extends StatefulWidget {
final FocusNode focusNode;
const Page1({Key key, this.focusNode}) : super(key: key);
void setFocus(BuildContext context) {
print("$focusNode requestFocus...");
FocusScope.of(context).requestFocus(focusNode);
}
#override
State<StatefulWidget> createState() {
return _Page1State();
}
}
class _Page1State extends State<Page1> {
TextEditingController name1 = TextEditingController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
nameApp(),
],
)
)
);
}
Widget nameApp(){
return Container(
margin: EdgeInsets.all(50.0),
//width: 185,
child: Center(
child: Row(
children: [
Container(
child: Text("Name :", style: TextStyle(fontSize: 15), ),
),
Container(
child: Flexible(
child: TextField(
focusNode: widget.focusNode,
controller: name1,
onTap: (){
name1.clear();
},
onChanged: (String str){
},
textAlign: TextAlign.center,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 5),
hintText: "Full Name",
hintStyle: TextStyle(fontSize: 14),
),
),
),
),
]
)
)
);
}
}
When user click on the bottom tab, I expect to see the textfield is in focus however nothing happen.
I noticed the method in child widget has been called:
flutter: FocusNode#419f4 requestFocus...
flutter: FocusNode#419f4(FOCUSED) requestFocus...
however the textfield is still not focus.
I've create a simple sample project for this and its works for me just fine.
Please check out my solution:
The HomePage:
import 'package:flutter/material.dart';
import 'package:focus_node/widgets/MyInputWidget.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'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FocusNode field1FocusNode = FocusNode(); //Create first FocusNode
FocusNode field2FocusNode = FocusNode(); //Create second FocusNode
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
child: MyInputWidget(
focusNode: field1FocusNode, //Provide the first FocusNode in the constructor
hint: "Email",
onEditCompleted: (){
FocusScope.of(context).requestFocus(field2FocusNode); //Request focus
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 35),
child: MyInputWidget(
focusNode: field2FocusNode, //Provide the second FocusNode
hint: "Password",
onEditCompleted: (){
FocusScope.of(context).requestFocus(field1FocusNode); //Request focus
},
),
)
],
),
),
);
}
}
The Custom Widget required focus:
class MyInputWidget extends StatefulWidget {
final FocusNode focusNode;
final String hint;
final VoidCallback onEditCompleted;
MyInputWidget({this.focusNode, this.hint, this.onEditCompleted});
#override
_MyInputWidgetState createState() => _MyInputWidgetState();
}
class _MyInputWidgetState extends State<MyInputWidget> {
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8),
child: TextField(
focusNode: widget.focusNode, //The FocusNode provided by the parent widget
decoration: InputDecoration(
hintText: widget.hint
),
onEditingComplete: widget.onEditCompleted,
),
);
}
}
Hope this helps.
I want to get value key which contains in Widget build(), but it says 'Undefined'. Also, I need to get this value to another class. How can I do it?
I`ve tried to just take this value, but it says undefined error
String newValue = s; // It says Undefined
I also tried to get this value to another class but this method gives more errors :c
myCard c = myCard();
String classValue = c.s; // It says 'Only static members can be accessed in initializers' and 'The getter 's' isn`t defined for the class 'myCard' '
Here`s part of main.dart file
class MyCard extends StatefulWidget {
#override
myCard createState() => myCard();
}
class myCard extends State<MyCard> {
int myCount = count - 1;
void click() {
setState(() {
print(titlecard);
Navigator.push(context, MaterialPageRoute(
builder: (context) => setNewText())
);
});
}
#override
Widget build(BuildContext context) {
Key s = Key(myCount.toString()); // I want to get this value
return Center(
key: s,
child: Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
leading: Icon(Icons.album),
title: Text(titlecard[int.parse(s)]),
subtitle: Text(textcard),
),
ButtonTheme.bar( // make buttons use the appropriate styles for cards
child: ButtonBar(
children: <Widget>[
FlatButton(
child: const Text('Change Text'),
onPressed: click,
),
FlatButton(
child: const Text('LISTEN'),
onPressed: () {
print(s);
},
),
],
),
),
],
),
),
);
}
}
class setNewText extends StatefulWidget {
#override
SetNewText createState() => SetNewText();
}
class SetNewText extends State<setNewText> {
myCard c = myCard();
HomePageState s = HomePageState();
String v = c.s; // To here
final titleController = TextEditingController();
final textController = TextEditingController();
final formkey = GlobalKey<FormState>();
List<String> titles = [''];
void _submit() {
setState(() {
if (formkey.currentState.validate()) {
formkey.currentState.save();
Navigator.pop(context);
titlecard.removeAt(count-s.myCount);
titlecard.insert(count-s.myCount, titleController.text);
textcard = textController.text;
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Change Title'),
),
body: Column(
children: <Widget>[
Card(
child: Padding(
padding: EdgeInsets.all(2.0),
child: Form(
key: formkey,
child: Column(
children: <Widget>[
TextFormField(
controller: titleController,
decoration: InputDecoration(
labelText: 'Title'
),
validator: (value) => value.length < 1 ? 'Invalid Title' : null,
onSaved: (value) => value = titleController.text,
),
TextFormField(
controller: textController,
decoration: InputDecoration(
labelText: 'Text'
),
validator: (text) => text.length < 1 ? 'Invalid Text' : null,
onSaved: (text) => text = textController.text,
)
],
),
),
),
),
FlatButton(
textColor: Colors.deepPurple,
child: Text('SUBMIT'),
onPressed: _submit,
),
],
)
);
}
}
Since you're just listening for values, one way of doing this is by listening to the value using Streams. You can initialize the class where the value can be Streamed and access it anywhere. Do note that once the Stream has been closed, you can only create a new Stream.
Here's a sample.
import 'dart:async';
import 'package:flutter/material.dart';
class SampleStream extends StatefulWidget {
const SampleStream({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<SampleStream> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<SampleStream> {
Counter counter = Counter();
#override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: counter.showCount,
builder: (context, AsyncSnapshot<int> snapshot) {
int count = snapshot.hasData ? snapshot.data! : 0;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Text(
'You clicked the button $count times'),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.plus_one),
onPressed: () {
counter.setCount(++count);
},
),
);
});
}
#override
void dispose() {
super.dispose();
counter.disposeCount();
}
}
class Counter {
final _count = StreamController<int>.broadcast();
Stream<int> get showCount => _count.stream;
setCount(int count) {
debugPrint('Stream sink: $count');
_count.sink.add(count);
}
disposeCount() {
_count.close();
}
}
In this demo, Counter was initialized in _MyHomePageState and can only be accessed in the same class. Calling counter.setCount(int) updates the stream and the stream value can be listened to using StreamBuilder to fetch the snapshot.
As title. It since that we can detect the drawer is opened, but is this possible to check it is closed or not? Thanks.
I have added this feature in Flutter 2.0.0. Make sure you are using Flutter SDK version >= 2.0.0 to use this.
Simply use a callback in Scaffold
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: NavDrawer(),
onDrawerChanged: (isOpen) {
// write your callback implementation here
print('drawer callback isOpen=$isOpen');
},
endDrawer: NavDrawerEnd(),
onEndDrawerChanged: (isOpen) {
// write your callback implementation here
print('end drawer callback isOpen=$isOpen');
},
body:
...
Pull request merged in 2.0.0: https://github.com/flutter/flutter/pull/67249
Happy coding!
Declare a GlobalKey to reference your drawer:
GlobalKey _drawerKey = GlobalKey();
Put the key in your Drawer:
drawer: Drawer(
key: _drawerKey,
Check if your drawer is visible:
final RenderBox box = _drawerKey.currentContext?.findRenderObject();
if (box != null){
//is visible
} else {
//not visible
}
You can copy paste run full code below
You can wrap Drawer with a StatefulWidget and put callback in initState() and dispose()
initState() will call widget.callback(true); means open
dispose() will call widget.callback(false); means close
Slide also work in this case
code snippet
drawer: CustomDrawer(
callback: (isOpen) {
print("isOpen ${isOpen}");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_isDrawerOpen = isOpen;
});
});
},
...
class CustomDrawer extends StatefulWidget {
CustomDrawer({
Key key,
this.elevation = 16.0,
this.child,
this.semanticLabel,
this.callback,
}) : assert(elevation != null && elevation >= 0.0),
super(key: key);
final double elevation;
final Widget child;
final String semanticLabel;
final DrawerCallback callback;
#override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
#override
void initState() {
if (widget.callback != null) {
widget.callback(true);
}
super.initState();
}
#override
void dispose() {
if (widget.callback != null) {
widget.callback(false);
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Drawer(
key: widget.key,
elevation: widget.elevation,
semanticLabel: widget.semanticLabel,
child: widget.child);
}
}
working demo
full code
import 'package:flutter/material.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> {
bool _isDrawerOpen = false;
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
drawer: CustomDrawer(
callback: (isOpen) {
print("isOpen ${isOpen}");
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_isDrawerOpen = isOpen;
});
});
},
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
child: Text('Drawer Header'),
decoration: BoxDecoration(
color: Colors.blue,
),
),
ListTile(
title: Text('Item 1'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
},
),
ListTile(
title: Text('Item 2'),
onTap: () {
// Update the state of the app.
// ...
},
),
],
),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Align(
alignment: Alignment.centerRight,
child: Text(
_isDrawerOpen.toString(),
),
),
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class CustomDrawer extends StatefulWidget {
CustomDrawer({
Key key,
this.elevation = 16.0,
this.child,
this.semanticLabel,
this.callback,
}) : assert(elevation != null && elevation >= 0.0),
super(key: key);
final double elevation;
final Widget child;
final String semanticLabel;
final DrawerCallback callback;
#override
_CustomDrawerState createState() => _CustomDrawerState();
}
class _CustomDrawerState extends State<CustomDrawer> {
#override
void initState() {
if (widget.callback != null) {
widget.callback(true);
}
super.initState();
}
#override
void dispose() {
if (widget.callback != null) {
widget.callback(false);
}
super.dispose();
}
#override
Widget build(BuildContext context) {
return Drawer(
key: widget.key,
elevation: widget.elevation,
semanticLabel: widget.semanticLabel,
child: widget.child);
}
}
class SecondRoute extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("route test"),
),
body: Text("SecondRoute"));
}
}
You can simply use onDrawerChanged for detecting if the drawer is opened or closed in the Scaffold widget.
Property :
{void Function(bool)? onDrawerChanged}
Type: void Function(bool)?
Optional callback that is called when the Scaffold.drawer is opened or closed.
Example :
#override Widget build(BuildContext context) {
return Scaffold(
onDrawerChanged:(val){
if(val){
setState(() {
//foo bar;
});
}else{
setState(() {
//foo bar;
});
}
},
drawer: Drawer(
child: Container(
)
));
}
When you click a Drawer Item where you will navigate to a new screen, there in the Navigator.push(..) call, you can add a .then(..) clause, and then know when the Drawer item Screen has been popped.
Here is the ListTile for a Drawer item which makes the Navigator.push(..) call when clicked , and the the associated .then(..) callback block:
ListTile(
title: Text('About App'),
onTap: () {
Navigator.push(
_ctxt,
MaterialPageRoute(builder: (context) => AboutScreen()),
).then(
(value) {
print('Drawer callback for About selection');
if (_onReadyCallback != null) {
_onReadyCallback();
}
},
);
}),
_onReadyCallback() represents a Function param you can pass in.
I found this is approach - of leveraging the .then() callback from a .push() call - to be a very useful concept to understand with Flutter in general.
Big thanks to the main 2 answers here:
Force Flutter navigator to reload state when popping
Here's the complete Drawer code:
Drawer drawer = Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Color(0xFF7FAD5F),
),
child: Text(App.NAME_MENU),
),
ListTile(
title: Text('About App'),
onTap: () {
Navigator.push(
_ctxt,
MaterialPageRoute(builder: (context) => AboutScreen()),
).then(
(value) {
print('Drawer callback for About selection');
if (_onReadyCallback != null) {
_onReadyCallback();
}
},
);
}),
],
),
);
I would recommend that you use this package : https://pub.dev/packages/visibility_detector.
Afterwards you should assign a GlobalKey, like _drawerKey for instance, to the Drawer widget, after which you would be able to detect when the drawer is closed like this:
VisibilityDetector(
key: _drawerKey,
child: Container(),
onVisibilityChanged: (info) {
if (info.visibleFraction == 0.0) {
// drawer not visible.
}
},
)
I would like to highlight the border of this card whenever is selected, so the user will see that specific card has been selected.
Try this !
The Result :
The Code :
import 'package:flutter/material.dart';
void main() => runApp(
new MaterialApp(
home: new MyApp(),
),
);
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("NonstopIO"),
),
body: new ListView.builder(
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
return new MyCustomWidget(
title: "Title $index",
subtitle: "$index",
);
},
),
);
}
}
class MyCustomWidget extends StatefulWidget {
final String title;
final String subtitle;
const MyCustomWidget({Key key, this.title, this.subtitle}) : super(key: key);
#override
_MyCustomWidgetState createState() => _MyCustomWidgetState();
}
class _MyCustomWidgetState extends State<MyCustomWidget> {
bool selected = false;
#override
Widget build(BuildContext context) {
return new Card(
shape: selected
? new RoundedRectangleBorder(
side: new BorderSide(color: Colors.blue, width: 2.0),
borderRadius: BorderRadius.circular(4.0))
: new RoundedRectangleBorder(
side: new BorderSide(color: Colors.white, width: 2.0),
borderRadius: BorderRadius.circular(4.0)),
child: new Padding(
padding: const EdgeInsets.all(4.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Text(widget.title),
new Text(widget.subtitle),
new Checkbox(
value: selected,
onChanged: (value) {
setState(() {
selected = value;
});
})
],
),
),
);
}
}
I found something useful and similar to what I would like to achieve.
Flutter - I want to select the card by onLongPress?
I'm trying to create a Radio in a showDialog, however the animation that occurs on Radio does not appear in showDialog.
For example: when tapped in foo2 nothing happens, and when you exit in showDialog and go back to it, foo2 is selected.
Below is the code and a gif showing what is happening:
import "package:flutter/material.dart";
void main() {
runApp(new ControlleApp());
}
class ControlleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: "My App",
home: new HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
HomePageState createState() => new HomePageState();
}
enum _RadioGroup {
foo1,
foo2
}
class HomePageState extends State<HomePage> {
_RadioGroup _itemType = _RadioGroup.foo1;
void changeItemType(_RadioGroup type) {
setState(() {
_itemType = type;
});
}
void showDemoDialog<T>({ BuildContext context, Widget child }) {
showDialog<T>(
context: context,
child: child,
);
}
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(backgroundColor: new Color(0xFF26C6DA)),
body: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new InkWell(
onTap: (){
showDemoDialog<String>(
context: context,
child: new SimpleDialog(
title: const Text("show"),
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo1,
onChanged: changeItemType
),
const Text("foo1"),
new Radio<_RadioGroup>(
groupValue: _itemType,
value: _RadioGroup.foo2,
onChanged: changeItemType
),
const Text("foo2"),
],
)
],
)
);
},
child: new Container(
margin: new EdgeInsets.only(top: 16.0, bottom: 8.0),
child: new Text("Show"),
),
)
],
),
)
);
}
}
Remember that components are immutable.
When you call showDialog, the content of that dialog won't change even if HomePage does.
The solution is easy. You need to refactor a bit your code to something like :
showDialog(
context: context,
builder: (context) => MyForm()
)
and instead of changing the state of HomePage, you instead change the state of MyForm.
example :
class Test extends StatelessWidget {
void onSubmit(String result) {
print(result);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () => showDialog(context: context, builder: (context) => MyForm(onSubmit: onSubmit)),
child: Text("dialog"),
),
),
);
}
}
typedef void MyFormCallback(String result);
class MyForm extends StatefulWidget {
final MyFormCallback onSubmit;
MyForm({this.onSubmit});
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
String value = "foo";
#override
Widget build(BuildContext context) {
return SimpleDialog(
title: Text("My form"),
children: <Widget>[
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "foo",
),
Radio(
groupValue: value,
onChanged: (value) => setState(() => this.value = value),
value: "bar",
),
FlatButton(
onPressed: () {
Navigator.pop(context);
widget.onSubmit(value);
},
child: new Text("submit"),
)
],
);
}
}