Reputation: 80
I have one class with home page and another class with Dialog (which appears when user clicks floating button).
There is a TextField on home page which I want to update (immediately) after picking something from Dropdown Menu in dialog (which is located very far away from TextField, and in another class). How can I tell flutter to rebuild that (parent) class?
It is not working for me, after applying changes as you wrote there are no options in my dropdown menu and it is not clickable.
DropdownButton<double>(
hint: Text("Specify sleep lenght"),
value: dropdownValue,
onChanged: onValueChange,
items: availableLengthHours.map((l) {
return DropdownMenuItem(
child: new Center(child: Text(l.toString())),
value: l,
);
}).toList(),
),
Upvotes: 1
Views: 1105
Reputation: 5743
Two things you need for this use-case
ValueNotifier<String>
: for only updating your Text
widget when value is picked.showDialog<String>
: dialog that return string value.Using ValueNotifier
will be efficient because you can target your Text widget and only rebuild it when value is changed. This will avoid unnecessary rebuild of some arbitrary Widget on your screen that doses not use this value.
Following is the working code for your reference: Run on DartPad
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: AppContent(),
);
}
}
class AppContent extends StatelessWidget {
final ValueNotifier<String> _textValue = ValueNotifier<String>('Default Text');
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Demo',),),
body: ChildWidget(_textValue),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog<String>(
context: context,
builder: (BuildContext dialogContext) {
return Dialog(
onValuePicked: (String value) {
Navigator.pop(context, value);
},
);
}).then((String value) {
if (value != null && value.isNotEmpty) {
_textValue.value = value;
}
});
},
),
);
}
}
class ChildWidget extends StatelessWidget {
final ValueNotifier<String> textValue;
ChildWidget(this.textValue);
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
builder: (BuildContext context, value, Widget child) {
return Text(value, style: Theme.of(context).textTheme.display2,);
},
valueListenable: textValue,
);
}
}
class Dialog extends StatelessWidget {
final ValueChanged<String> onValuePicked;
static const List<String> items = <String>[
'item 1',
'item 2',
'item 3',
'item 4'
];
static String dropdownValue = items[0];
Dialog({Key key, this.onValuePicked}) : super(key: key);
@override
Widget build(BuildContext context) => AlertDialog(
title: new Text('Item picker'),
content: DropdownButton<String>(
value: dropdownValue,
onChanged: onValuePicked,
items: items.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
},
).toList(),
),
);
}
Upvotes: 2
Reputation: 3003
Jacek, not sure what you've tried but one way to achieve what you want is by using your dialog selection as a result of Navigator.push
. Here is a quick example,
class MyApp extends StatefulWidget {
@override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
String text = 'Original text';
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Test'),),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(
left: 10.0, right: 10.0),
child: Column(children: <Widget>[
Text(text, style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold)),
]))
])),
floatingActionButton: Builder( builder: (context) => FloatingActionButton(
child: Icon(Icons.refresh),
onPressed: () async {
update(context);
},
)),
),
);
}
update(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => Dialog()),
);
setState(() {
text= result;
});
}
}
class Dialog extends StatelessWidget {
String dropdownValue = 'Updated item 1';
@override
Widget build(BuildContext context) {
return AlertDialog(
title: new Text('Test dialog'),
content: DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.arrow_downward),
iconSize: 24,
elevation: 16,
style: TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String newValue) {
Navigator.pop(context, newValue);
},
items: <String>[
'Updated item 1',
'Updated item 2',
'Updated item 3',
'Updated item 4'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
actions: <Widget>[
// usually buttons at the bottom of the dialog
new FlatButton(
child: new Text('Cancel'),
onPressed: () {
Navigator.pop(context, true);
},
)
],
);
}
}
Hope this heps.
Upvotes: 4