【问题标题】:Flutter: bloc, how to show an alert dialogFlutter:bloc,如何显示警报对话框
【发布时间】:2019-04-21 14:05:59
【问题描述】:

我是集团模式和流媒体的新手。我想在按下按钮时显示一个警报对话框,但我找不到办法。其实我的代码是:

Widget button() {
  return RaisedButton(
      child: Text('Show alert'),
      color: Colors.blue[700],
      textColor: Colors.white,
      onPressed: () {
        bloc.submit();
      });
   }



return Scaffold(
        appBar: AppBar(
          title: Text("Title"),
        ),
        body: StreamBuilder(
            stream: bloc.getAlert,
            builder: (context, snapshot) {
              if (snapshot.hasData) {
                return Text("I have Dataaaaaa ${snapshot.data}");
              } else
                return ListView(
                  children: <Widget>[
                    Container(
                     button()
                    )
                ...

还有 BLoC:

final _submissionController = StreamController();
Stream get submissionStream=> _submissionController.stream;
Sink get submissionSink=> _submissionController.sink;

我试图做类似的事情:

Widget button() {
  return StreamBuilder(
stream: submissionStream
builder: (context, snapshot){
if (snapshot.hasData){
return showDialog(...)
}else
 return RaisedButton(
      child: Text('Show alert'),
      color: Colors.blue[700],
      textColor: Colors.white,
      onPressed: () {
        bloc.submit();
      });
   }

但是,当然,它没有用。

【问题讨论】:

    标签: view stream flutter alert


    【解决方案1】:

    构建工作时不能显示对话框。当您有新数据时,您将创建一个新的小部件。在这种情况下不使用流可能对你更好,但如果有必要,你应该使用

    WidgetsBinding.instance.addPostFrameCallback((_) => yourFunction(context));

    Future.microtask(() => showDialogFunction(context));

    在你的如果

    if (snapshot.hasData) { WidgetsBinding.instance.addPostFrameCallback((_) => showDialogFunction(context)); }

    此代码将在构建方法之后启动,因此对话框将立即显示。

    Bloc 函数总是返回小部件,所以当流有数据时总是返回 button() 或不同的小部件

    【讨论】:

    • 是的,在这些情况下最好避免使用 BLoC,但由于我对它很陌生,我仍然需要学习如何正确使用它。但是您建议的方式有效,所以谢谢!
    【解决方案2】:

    这就是我所做的,这可能是错误的,因为我也是 Flutter 的新手。但适用于我的场景。

    Widget build(BuildContext context) {
    final authBloc = BlocProvider.of<AuthBloc>(context);
    
    authBloc.outServerResponse.listen((serverResponse) {
      if (serverResponse.status == 'success') {
        _navigateToLogin();
      } else {
        _showSnakBar(serverResponse.message);
      }
    });
    .... Rest of the code which returns the widget, 
    which in my case is form widget with button for submitting as follows,
    onPressed: () {
      if (_formKey.currentState.validate()) {
          _formKey.currentState.save();
          authBloc.processRegister.add(_registrationData.toMap());
      }
    }
    

    outServerResponse 是 API POST 调用完成后输出的流。

    authBloc.processRegister 是将表单数据传递给我的 Auth API 服务提供程序的输入接收器。

    _nagivateToLogin & _showSnakBar 是简单的函数

    _navigateToLogin() {
          Navigator.of(context).pop();
    }
    
    _showSnakBar(String msg) {
         Scaffold.of(context).showSnackBar(
          SnackBar(
            content: Text(msg),
          ),
         );
     }
    

    【讨论】:

    • 似乎对于每个构建,流都会一次又一次地订阅 'authBloc.outServerResponse.listen' 我认为订阅必须关闭。
    • @RideSun 是真的。如果它是有状态的并在 dispose 时关闭,最好将其置于 initstate 中。
    【解决方案3】:

    您可以使用BlocListener 显示对话框、Snackbars 或导航到新页面。

    使用这种方法,您可能希望重构以依赖 bloc 状态而不是直接访问流。

    return Scaffold(
      appBar: AppBar(
        title: Text("Title"),
      ),
      body: BlocProvider<YourBloc>(
        create: () => YourBloc(),
        child: Stack([
          SnackbarManager(),
          YourScreen(),
        ]),
      ),
    );
    ...
    
    /// This is basically an empty UI widget that only
    /// manages the snackbar
    class SnackbarManager extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return BlocListener<YourBloc, YourBlocState>(
          listener: (context, state) {
            if (state.hasMyData) {
              Scaffold.of(context).showSnackBar(SnackBar(
                content:
                    Text("I got data"),
              ));
            }
          },
          child: Container(),
        );
      }
    }
    

    【讨论】:

    • 我正在尝试使用 bloc 在对话框中显示未来的数据,我不明白您在使用堆栈时的意思[SnackBarManager(), screen()]在这种情况下是否堆栈允许screen和blockListener同时运行?
    【解决方案4】:

    这个过程对我有用。 我在返回小部件之前调用了我的对话框

    Future.microtask(() => showLoginSuccess(BuildContext context));

    【讨论】:

      【解决方案5】:

      我知道我迟到了,但也许这会对某人有所帮助。 我目前正在自己​​学习 BLoC 并遇到了类似的问题。

      首先,我想推荐flutter_bloc package from pub.dev。 它包含可以帮助您解决此问题的小部件,例如 BlocListenerBlocConsumer

      如果您想不使用它,您可以尝试分别使用StatefulWidgetlisten 并使用您的逻辑来显示对话框。 (还要确保您的流在我的示例中进行广播,因此它可以有多个侦听器)

      我做了一个例子,你可以复制到dartpad.dev/flutter

      import 'dart:async';
      import 'package:flutter/material.dart';
      
      final myStream = StreamController<bool>.broadcast();
      
      void main() {
        runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return MaterialApp(
            theme: ThemeData.dark(),
            debugShowCheckedModeBanner: false,
            home: Scaffold(
              body: Center(
                child: MyWidget(),
              ),
            ),
          );
        }
      }
      
      class MyWidget extends StatefulWidget {
        @override
        _MyWidgetState createState() => _MyWidgetState();
      }
      
      class _MyWidgetState extends State<MyWidget> {
      
        initState() {
          super.initState();
          myStream.stream.listen((show){
            if(show)
              showDialog(
              barrierDismissible: false,
              context: context,
              builder: (context) {
                return AlertDialog(
                  title: Text('MyDialog'),
                  actions: [
                    TextButton(
                      child: Text('Close'),
                      onPressed: (){
                        myStream.sink.add(false);
                    }),
                  ]
                );
              }
            );
            if(!show) {
              Navigator.pop(context);
            }
          });
        }
      
        @override
        Widget build(BuildContext context) {
          return Center(child: ElevatedButton(
            child: Text('Show Alert'),
          onPressed: (){
            myStream.sink.add(true);
          }));
        }
      }
      

      【讨论】:

        【解决方案6】:

        我通过如下维护两个上下文来解决它 **

        BlocProvider of type A  ==>widget class B(showdialog(context:context,builder(context2){
        Blocprvider.value(value:Blocprovider.of<A>.context)
        child:BlocListener(
        listner(context2,state)
        {// 
         your works
        //}
        child:AlertDialog( some widgets
        a button function ()=> context.read<A>().function or property name
        //
        

        1.这里我们称之为旧上下文实际上它是向提供者注册的,2.上下文2仅用于构建新的构建器小部件。 3.因此我们通过导航获得 bloc 并在导航警报小部件中访问而无需创建它

        【讨论】:

          猜你喜欢
          • 2023-03-13
          • 2020-01-14
          • 1970-01-01
          • 1970-01-01
          • 2023-03-25
          • 1970-01-01
          • 2011-01-08
          • 1970-01-01
          相关资源
          最近更新 更多