无法在 showModalBottomSheet 中反映更新的父状态

Unable to reflect updated parent state in showModalBottomSheet

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

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

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

** 编辑 **

我看过 但它并没有真正解释如何将状态从父小部件向下传递到 showBottomModalSheet 小部件,它只是展示了如何在 [=23] 中管理状态=] 小部件。我的目标是让 main 内的状态发生变化,以便能够在 showBottomModalSheet.

内进行选择

main.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'),
                      )),
              ])
            ],
          );
  }
}




TextController 是 listenable。您可以将您的 Column 包裹在两个 ValueListenable 中(每个控制器一个),这将告诉该小部件在它们的值更新时更新。

@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 将是我的第一选择,然后可能会使用钩子来简化它们的使用。