简体   繁体   中英

Flutter - Rebuild parent after calling setState in grandchildren

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(),
),

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.

Two things you need for this use-case

  1. ValueNotifier<String> : for only updating your Text widget when value is picked.
  2. 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(),
        ),
      );
}

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM