【问题标题】:How to update state of a ModalBottomSheet in Flutter?如何在 Flutter 中更新 ModalBottomSheet 的状态?
【发布时间】:2019-02-24 03:30:16
【问题描述】:

这段代码非常简单:显示一个模态底部工作表,当用户单击按钮时,它将工作表的高度增加 10。

但是什么也没发生。实际上,只有当用户用手指“滑动”底部工作表时,它才会更新其大小(我相信滑动会导致工作表上出现内部 setState)。

我的问题是:如何调用 ModalBottomSheet 的更新状态?

showModalBottomSheet(
    context: context,
    builder: (context) {
      return Container(
        height: heightOfModalBottomSheet,
        child: RaisedButton(

            onPressed: () {
              setState(() {
                heightOfModalBottomSheet += 10;
              });

            }),
      );
    });

【问题讨论】:

  • 您是否遇到任何错误?因为它对我有用
  • 使用 ChangeNotifier 提供程序。它有效。

标签: flutter


【解决方案1】:

您可以使用 Flutter 的 StatefulBuilder 来包装您的 ModalBottomSheet,如下所示:

showModalBottomSheet(
    context: context,
    builder: (context) {
      return StatefulBuilder(
          builder: (BuildContext context, StateSetter setState /*You can rename this!*/) {
        return Container(
          height: heightOfModalBottomSheet,
          child: RaisedButton(onPressed: () {
            setState(() {
              heightOfModalBottomSheet += 10;
            });
          }),
        );
      });
});

请注意,新的setState 将覆盖您的主小部件setState,但请确保您可以重命名它,以便能够设置父小部件和模式的状态

//This sets modal state
setModalState(() {
    heightOfModalBottomSheet += 10;
});
//This sets parent widget state
setState(() {
     heightOfModalBottomSheet += 10;
});

【讨论】:

  • 这应该是正确的答案。非常感谢您,先生,您为我节省了很多时间(我实际上考虑过改变我的方法,但我又进行了一次搜索以找到解决方案,而您在页面底部带有金票))
  • 优秀的答案。这使我能够拥有一个持久的 BottomSheet,我可以将其设置为自定义大小,借助 StatefulBuilder 和 StateSetter,我可以将其设置为全屏并返回动画。使用全局 setState 会强制 BottomSheet 使用其默认的打开过渡重绘。
  • 你能告诉我为什么我们需要使用 StatefulBuilder 来设置状态 insde showModalBottomSheet 以及为什么正常的 setState 不起作用?
  • 请将此选为正确答案,这样人们在测试标记为正确的错误代码时就不会出错。
  • @mmahgoub mmahgoub - 顺便说一句,我们如何将变量值从父屏幕更新到底部表和底部表到父屏幕?请建议。非常感谢。
【解决方案2】:

您也许可以使用ScaffoldState 中的showBottomSheet。阅读更多 here 关于这个 showBottomSheet 的信息。

这将显示bottomSheet 并返回一个控制器PersistentBottomSheetController。使用此控制器,您可以调用controller.SetState((){}),它将重新渲染底部表。

这是一个例子

PersistentBottomSheetController _controller; // <------ Instance variable
final _scaffoldKey = GlobalKey<ScaffoldState>(); // <---- Another instance variable
.
.
.
void _incrementBottomSheet(){
    _controller.setState(
        (){
            heightOfModalBottomSheet += 10;
        }
    )
}
.
void _createBottomSheet() async{
  _controller = await _scaffoldKey.currentState.showBottomSheet(
        context: context,
        builder: (context) {
           return Container(
               height: heightOfModalBottomSheet,
               child: RaisedButton(
               onPressed: () {
                  _incrementBottomSheet()
              }),
         );
      });
}

【讨论】:

  • 除了bottomSheetmodalBottomSheet不一样。
  • 仅供参考,下面有一个更正确的答案(最高投票),不需要将您的方法从模态底部表更改为持久底部表。
【解决方案3】:

截图:


创建一个类:

class MyBottomSheet extends StatefulWidget {
  @override
  _MyBottomSheetState createState() => _MyBottomSheetState();
}

class _MyBottomSheetState extends State<MyBottomSheet> {
  bool _flag = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        FlutterLogo(
          size: 300,
          style: FlutterLogoStyle.stacked,
          textColor: _flag ? Colors.black : Colors.red,
        ),
        RaisedButton(
          onPressed: () => setState(() => _flag = !_flag),
          child: Text('Change Color'),
        )
      ],
    );
  }
}

用法:

showModalBottomSheet(
  context: context,
  builder: (_) => MyBottomSheet(),
);

【讨论】:

  • 关键是在代表BottomSheet 内容的小部件上调用setState(在此示例中为MyBottomSheet),而不是在打开BottomSheet 的小部件上调用showModalBottomSheet(带有showModalBottomSheet) .有关完整示例,请参见以下 DartPad:dartpad.dev/896181cf93370d29c7c0015207fde12c
  • @Faber 我相信我写了同样的东西。
  • 我同意您的示例也可以正常工作。感谢那。但是,当我试图理解您的示例时,我不清楚我需要从哪里调用 showModalBottomSheet,因此我将链接添加到一个可运行的示例。
  • @Faber 好的。好吧,例如,您只需要从 Button onPress 调用 showModalBottomSheet 方法即可。
  • 对于一些我无法让有状态的构建器按照其他答案中的建议工作。这很有效,并为我提供了更清晰的单独代码。
【解决方案4】:

请参考下面的工作代码。我为showModalBottomSheet 创建了一个新的有状态小部件(ModalBottomSheet)。在按下按钮时,我们正在重建 ModalBottomSheet only,它现在更干净了。如果需要动画来改变高度,我们可以使用AnimationController

import 'dart:async';
import 'package:flutter/material.dart';

class ModalBottomSheet extends StatefulWidget {
  _ModalBottomSheetState createState() => _ModalBottomSheetState();
}

class _ModalBottomSheetState extends State<ModalBottomSheet>
    with SingleTickerProviderStateMixin {
  var heightOfModalBottomSheet = 100.0;

  Widget build(BuildContext context) {
    return Container(
      height: heightOfModalBottomSheet,
      child: RaisedButton(
          child: Text("Press"),
          onPressed: () {
            heightOfModalBottomSheet += 100;
            setState(() {});
          }),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    Future(() => showModalBottomSheet(
        context: context,
        builder: (context) {
          return ModalBottomSheet();
        }));
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Modal example"),
      ),
    );
  }
}

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(title: 'Flutter Demo', home: new MyHomePage());
  }
}

【讨论】:

  • 这不是设置当前模式的状态,而是在旧模式上创建一个新模式。每次按下按钮时都会产生丑陋的效果。
  • 我同意...这就是为什么我在增加高度之前关闭前一个...但是仍然会有闪烁效果...
  • 这不会更新 modal itselt 的状态,只会更新其中的内容。但我相信这是一个很好的解决方法,适用于许多情况。谢谢!
【解决方案5】:

为 showModalBottomSheet() 创建一个单独的 StatefulWidget,比如

 showModalBottomSheet(
    context: context,
    builder: (ctx) {
      return MapBottomSheet();
    });

底页状态小部件

class MapBottomSheet extends StatefulWidget {
  @override
  _MapBottomSheetState createState() => _MapBottomSheetState();
}

class _MapBottomSheetState extends State<MapBottomSheet> {
  List<String> places = [];

  void _setPlaces(String place) {
    setState(() {
      places.add(place);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.black12,
      child: Column(
        children: [
          AppTextField(
            hint: "Search",
            onEditingComplete: () {},
            onChanged: (String text) {},
            onSubmitted: (String text) async {
              // Await the http get response, then decode the json-formatted response.
              var response = await http.get(Uri.parse(
                  'https://api.mapbox.com/geocoding/v5/mapbox.places/$text.json?access_token=pk.eyJ1IjoidjNyc2lvbjkiLCJhIjoiY2ttNnZldmk1MHM2ODJxanh1ZHZqa2I3ZCJ9.e8pZsg87rHx9FSM0pDDtlA&country=PK&fuzzyMatch=false&place=park'));
              if (response.statusCode == 200) {
                Map<String, dynamic> data = jsonDecode(response.body);
                print(data.toString());

                List<dynamic> features = data['features'];

                features.forEach((dynamic feature) {
                  setState(() {
                    _setPlaces(feature['place_name']);
                  });
                });
              } else {
                print('Request failed with status: ${response.statusCode}.');
              }
            },
          ),
          Expanded(
            child: Container(
              height: 250.0,
              width: double.infinity,
              child: ListView.builder(
                  itemCount: places.length,
                  itemBuilder: (ctx, idx) {
                    return Container(
                      child: Text(places[idx]),
                    );
                  }),
            ),
          ),
        ],
      ),
    );
    
  }
}

【讨论】:

    猜你喜欢
    • 2021-02-02
    • 2021-02-13
    • 2020-10-17
    • 2022-08-03
    • 2018-10-16
    • 2020-05-07
    • 1970-01-01
    • 2020-10-10
    • 1970-01-01
    相关资源
    最近更新 更多