【问题标题】:Unable to reflect updated parent state in showModalBottomSheet无法在 showModalBottomSheet 中反映更新的父状态
【发布时间】:2021-11-26 22:46:23
【问题描述】:

我对 Flutter 比较陌生,虽然我真的很喜欢它,但我正在努力寻找一种方法来让父级中的状态值在 showModalBottomSheet 中更新。我想我理解的问题是,当它们在父级中更改时,这些值没有反映在 showModalBottomSheet 中,因为当状态更新时 showModalBottomSheet 不会重建。

我将titlecontent 存储在父级中,因为我还希望将其用于编辑和创建待办事项。我认为showModalBottomSheet 可以为两者共享。我在模拟器上附上一张图片。我期望的是,当title 发生变化(即不再是空字符串)时,“添加待办事项”按钮应该会启用,但它目前保持禁用状态,除非我关闭模式并重新打开它。

任何帮助或见解将不胜感激。下面是我的main.dart 文件中的代码,该文件具有showModalBottomSheet 并具有需要传递的状态值。 NewToDo 包含模式中的文本字段,用于捕获值并相应地更新 main 中的状态。

** 编辑 **

我见过this link,但它并没有真正解释如何将状态从父小部件传递到showBottomModalSheet 小部件,它只是展示了如何在showBottomModalSheet 小部件中管理状态。我的目标是从main 中更改状态,以便能够在showBottomModalSheet 中选择。

ma​​in.dart

import 'package:flutter/material.dart';
import './todoitem.dart';
import './todolist.dart';
import 'classes/todo.dart';
import './newtodo.dart';


void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'To Do Homie',
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: const MyHomePage(title: "It's To Do's My Guy"),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    Key? key, 
    required this.title,
  }) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String content = '';
  String title = '';
  int maxId = 0;
  ToDo? _todo;
  final titleController = TextEditingController();
  final contentController = TextEditingController();
  List<ToDo> _todos = [];

  void _addTodo(){

    final todo = ToDo ( 
      title: title,
      id: maxId,  
      isDone: false,
      content: content
    );

    if (_todo != null){
      setState(() {
        _todos[_todos.indexOf(_todo!)] = todo;
      });
    } else {
      setState(() {
        _todos.add(todo);
      });
    }

    setState(() {
      content = '';
      maxId = maxId++;
      title = '';
      _todo = null;
    });

    contentController.text = '';
    titleController.text = '';
    
  }

  @override
  void initState() {
    super.initState();
    titleController.addListener(_handleTitleChange);
    contentController.addListener(_handleContentChange);
    futureAlbum = fetchAlbum();
  }

  void _handleTitleChange() {
    setState(() {
      title = titleController.text;
    });
  }

  void _handleContentChange() {
    setState(() {
      content = contentController.text;
    });
  }

  void _editTodo(ToDo todoitem){
    setState(() {
      _todo = todoitem;
      content = todoitem.content;
      title = todoitem.title;
    });
    contentController.text = todoitem.content;
    titleController.text = todoitem.title;
  }

  void _deleteToDo(ToDo todoitem){
    setState(() {
      _todos = List.from(_todos)..removeAt(_todos.indexOf(todoitem));
    });
  }

  void _clear(){
    contentController.text = '';
    titleController.text = '';
    setState(() {
      content = '';
      title = '';
      _todo = null;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: SingleChildScrollView( 
        child: Center(
          child: Container(
            alignment: Alignment.topCenter,
            child: ToDoList(_todos, _editTodo, _deleteToDo)
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showModalBottomSheet<void>(
            context: context,
            builder: (BuildContext context) {
              print(context);
              return Container(child:NewToDo(titleController, contentController, _addTodo, _clear, _todo),);
            });
        },
        child: const Icon(Icons.add),
        backgroundColor: Colors.deepPurple,
      ),
    );
  }
}

NewToDo.dart

import 'package:flutter/material.dart';
import './classes/todo.dart';

class NewToDo extends StatelessWidget {

  final Function _addTodo;
  final Function _clear;
  final ToDo? _todo;
  final TextEditingController titleController;
  final TextEditingController contentController;

  const NewToDo(this.titleController, this.contentController, this._addTodo, this._clear, this._todo, {Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return 
          Column(children: [
              TextField(
                decoration: const InputDecoration(
                  labelText: 'Title',
                ),
                controller: titleController,
                autofocus: true,
              ),
              TextField(
                decoration: const InputDecoration(
                  labelText: 'Details',
                ),
               controller: contentController,
               autofocus: true,
              ),
               ButtonBar(
                  alignment: MainAxisAlignment.center,
                  children: [
                    ElevatedButton(
                      onPressed: titleController.text.isNotEmpty ? () => _addTodo() : null, 
                      child: Text(_todo != null ? 'Edit To Do' : 'Add To Do'),
                      style: ButtonStyle(
                        backgroundColor: titleController.text.isNotEmpty ? MaterialStateProperty.all<Color>(Colors.deepPurple) : null,
                        overlayColor: MaterialStateProperty.all<Color>(Colors.purple), 
                      ),
                    ),
                    Visibility (
                      visible: titleController.text.isNotEmpty || contentController.text.isNotEmpty,
                      child: ElevatedButton(
                        onPressed: () => _clear(), 
                        child: const Text('Clear'),
                      )),
              ])
            ],
          );
  }
}




【问题讨论】:

  • 嘿,也许这可能会有所帮助stackoverflow.com/a/56972160/11212287
  • @AkshayDoshi 感谢您的链接。我之前检查过那个,但它并没有真正解释如何将状态值从父小部件传递到showModalBottomSheet,它只是展示了如何在其中管理它自己的状态。

标签: flutter dart flutter-showmodalbottomsheet


【解决方案1】:

TextController 是 listenable。您可以将您的 Column 包装在两个 ValueListenables 中(每个控制器一个),这将告诉该小部件在其值更新时进行更新。

@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
  valueListenable: contentController,
  builder: (context, _content, child) {
    return ValueListenableBuilder(
      valueListenable: titleController,
      builder: (context, _title, child) {
        return Column(
          children: [
            TextField(
              decoration: const InputDecoration(
                labelText: 'Title',
              ),
              controller: titleController,
              autofocus: true,
            ),
            TextField(
              decoration: const InputDecoration(
                labelText: 'Details',
              ),
              controller: contentController,
              autofocus: true,
            ),
            ButtonBar(
              alignment: MainAxisAlignment.center,
              children: [
                ElevatedButton(
                  onPressed:
                      titleController.text.isNotEmpty ? () => _addTodo() : null,
                  child: Text(_todo != null ? 'Edit To Do' : 'Add To Do'),
                  style: ButtonStyle(
                    backgroundColor: titleController.text.isNotEmpty
                        ? MaterialStateProperty.all<Color>(Colors.deepPurple)
                        : null,
                    overlayColor: MaterialStateProperty.all<Color>(Colors.purple),
                  ),
                ),
                Visibility(
                  visible: titleController.text.isNotEmpty ||
                      contentController.text.isNotEmpty,
                  child: ElevatedButton(
                    onPressed: () => _clear(),
                    child: const Text('Clear'),
                  ),
                ),
              ],
            )
          ],
        );
      },
    );
  },
);

我能想到的另一个更通用的替代方法是使用 Provider(或者,如果您足够熟悉,可以使用常规 InheritedWidgets)及其自述文件中建议的模式:

class Example extends StatefulWidget {
  const Example({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  ExampleState createState() => ExampleState();
}

class ExampleState extends State<Example> {
  int _count;

  void increment() {
    setState(() {
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Provider.value(
      value: _count,
      child: Provider.value(
        value: this,
        child: widget.child,
      ),
    );
  }
}

它建议像这样读取计数:

return Text(context.watch<int>().toString());

除了我猜你可以通过将_count 替换为this 来引用整个有状态的小部件,从而将小部件的整个状态提供给后代。不知道是否推荐。

ValueListenables 将是我的首选,然后可能会使用挂钩来简化它们的使用。

【讨论】:

  • 我最终选择了ValueListenableBuilder 选项,因为我首先尝试了它并且效果很好。你是救生员!
猜你喜欢
  • 2021-02-09
  • 1970-01-01
  • 2022-01-16
  • 2021-07-26
  • 1970-01-01
  • 2019-08-11
  • 2018-11-03
  • 2015-05-21
  • 1970-01-01
相关资源
最近更新 更多