我正在试图找到通过Snackbar显示来自更改通告程序模型和提供程序的错误的最佳方式.

你有什么内置的方法或建议可以帮我吗?

我发现这种方法是有效的,但我不知道它是否正确.

假设我有一个简单的页面,我想在其中显示一个对象列表和一个从api检索这些对象的模型.在发生错误的情况下,我会通知一个错误字符串,我想用一个快捷键显示该错误.

page.dart

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

class Page extends StatefulWidget {
  Page({Key key}) : super(key: key);

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

class _PageState extends State< Page > {

  @override
  void initState(){
    super.initState();
    Provider.of<Model>(context, listen: false).load();
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    Provider.of< Model >(context, listen: false).addListener(_listenForErrors);
  }

  @override
  Widget build(BuildContext context){
    super.build(context);
    return Scaffold(
      appBar: AppBar(),
      body: Consumer<Model>(
          builder: (context, model, child){
  
            if(model.elements != null){
              ...list
            }
            else return LoadingWidget();
          }
        )
      )
    );
  }



  void _listenForErrors(){
    final error = Provider.of<Model>(context, listen: false).error;
    if (error != null) {
      Scaffold.of(context)
        ..hideCurrentSnackBar()
        ..showSnackBar(
          SnackBar(
            backgroundColor: Colors.red[600],
            content: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Icon(Icons.error),
                Expanded(child: Padding( padding:EdgeInsets.only(left:16), child:Text(error) )),
              ],
            ),
          ),
        );
    }
  }

  @override
  void dispose() { 
        Provider.of<PushNotificationModel>(context, listen: false).removeListener(_listenForErrors);
    super.dispose();
  }

}

page_model.dart

import 'package:flutter/foundation.dart';

class BrickModel extends ChangeNotifier {

  List<String> _elements;
  List<String> get elements => _elements;

  String _error;
  String get error => _error;

  Future<void> load() async {
    try{
      final elements = await someApiCall();
      _elements = [..._elements, ...elements];
    }
    catch(e) {
      _error = e.toString();
    }
    finally {
      notifyListeners();
    }
  }

}

谢谢

推荐答案

Edit 2022

如果有人感兴趣,我也为river pod移植(并重新制作)了这个包

https://pub.dev/packages/riverpod_messages/versions/1.0.0

EDIT 2020-06-05

我开发了一种稍微好一点的方法来承受这种情况.

可以在This repo on github处找到它,这样您就可以在那里看到实现,或者使用这个包将其放入您的pubspec.yaml中

 provider_utilities:
    git:
      url: https://github.com/quantosapplications/flutter_provider_utilities.git

因此,当您需要向视图显示消息时,您可以:

  1. 将您的ChangeNotifier扩展为MessageNotifierMixin,这给您的ChangeNotifier提供了两个属性errorinfo以及两个方法notifyError()notifyInfo().

  2. 使用MessageListener包装您的Scaffold,该MessageListener将在调用NotifyError()或NotifyInfo()时显示SnackBar

我给你举个例子:

ChangeNotifier

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

class MyNotifier extends ChangeNotifier with MessageNotifierMixin {

  List<String> _properties = [];
  List<String> get properties => _properties;

  Future<void> load() async {

    try {
      /// Do some network calls or something else
      await Future.delayed(Duration(seconds: 1), (){

        _properties = ["Item 1", "Item 2", "Item 3"];
        notifyInfo('Successfully called load() method');

      });
    }
    catch(e) {
      notifyError('Error calling load() method');
    }

  }

}

View

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_utilities/provider_utilities.dart';

import 'notifier.dart';

class View extends StatefulWidget {
  View({Key key}) : super(key: key);

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

class _ViewState extends State<View> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: MessageListener<MyNotifier>(
        child: Selector<MyNotifier, List<String>>(
          selector: (ctx, model) => model.properties,
          builder: (ctx, properties, child) => ListView.builder(
            itemCount: properties.length,
            itemBuilder: (ctx, index) => ListTile(
              title: Text(properties[index])
            ),
          ),
        )
      )
    );
  }
}

OLD ANSWER

非常感谢.

也许我找到了一个更简单的方法来处理这个问题,使用消费者强大的属性"child".

使用自定义的无状态小部件(我称之为ErrorListener,但它可以更改:)

class ErrorListener<T extends ErrorNotifierMixin> extends StatelessWidget {

  final Widget child;

  const ErrorListener({Key key, @required this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Consumer<T>(
      builder: (context, model, child){
        
        //here we listen for errors
        if (model.error != null) { 
          WidgetsBinding.instance.addPostFrameCallback((_){
             _handleError(context, model); });
        }

        // here we return child!
        return child;
      },
      child: child
    );
  }


  // this method will be called anytime an error occurs
  // it shows a snackbar but it could do anything you want
  void _handleError(BuildContext context, T model) {
    Scaffold.of(context)
    ..hideCurrentSnackBar()
    ..showSnackBar(
      SnackBar(
        backgroundColor: Colors.red[600],
        content: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Icon(Icons.error),
            Expanded(child: Padding( padding:EdgeInsets.only(left:16), child:Text(model.error) )),
          ],
        ),
      ),
    );

    // this will clear the error on model because it has been handled
    model.clearError();
  }
}

如果你想使用snackbar,这个小部件必须放在脚手架下.

我在这里使用mixin来确保模型有error属性和clarError()方法.

mixin ErrorNotifierMixin on ChangeNotifier {
  String _error;
  String get error => _error;

  void notifyError(dynamic error) {
    _error = error.toString();
    notifyListeners();
  }

  void clearError() {
    _error = null;
  }
}

举个例子,我们可以用这种方式

class _PageState extends State<Page> {

   // ...

@override 
  Widget build(BuildContext context) =>
    ChangeNotifierProvider(
      builder: (context) => MyModel(),
      child: Scaffold(
        body: ErrorListener<MyModel>(
          child: MyBody()
        )
      )
    );

}

Dart相关问答推荐

我们应该将多少代码放入构造函数中?

Flutter 网页打开网址并获取数据

Flutter - 对 Cloud Firestore 进行排序

Dart/Flutter ffi(外部函数接口)本机回调,例如:sqlite3_exec

如何在 Dart 中创建一个空 Set

Flutter Dismissible 坚持必须从树中删除列表项

NoSuchMethodError:在 null 上调用了方法validate

Flatter Container()与SizedBox()的虚拟空容器

泛型类型Dart上的调用方法

在 Flutter 的 TextFormField 中管理事件

'dart:async' 的函数 `runZoned` 的用途

Visual Studio 的 Dart 插件

dart 如何创建、监听和发出自定义事件?

如何在dart中获得一周的开始或结束

如何将任意数据从 Dart polymer Web 组件传递到单击事件处理函数

如何从 Dart 中的 forEach 循环返回?

Dart MD5 字符串

如何判断一个对象是否是一个类的实例

Dart - 如何对 Map 的键进行排序

在 Dart 中创建具有可变数量的参数或参数的函数