如何在Flutter中使用提供程序显示ChangeNotifier中的错误

时间:2019-11-03 22:11:21

标签: flutter dart flutter-layout flutter-provider provider-model

我正在尝试找到最佳方法,以通过Snackbar从带有提供程序的更改通知程序模型中显示错误。

有没有内置的方法或建议可以帮助我?

我发现这种方式有效,但我不知道它是否正确。

假设我有一个简单的Page要在其中显示对象列表,而Model则需要从api检索这些对象。如果发生错误,我会通知错误字符串,并希望通过SnackBar显示该错误。

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();
    }
  }

}

谢谢

2 个答案:

答案 0 :(得分:1)

谢谢。

也许我找到了一种更简单的方法来处理此问题,即使用Consumer强大的属性“ 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();
  }
}

如果您想使用小吃店,则必须将此小部件放在支架下。

我在这里使用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()
        )
      )
    );

}

答案 1 :(得分:0)

您可以创建自定义FormatException: Invalid length, must be multiple of four (at character 183) ...jLTQxMDctYjUzNC1hOGZiOTNhMzEwNzAiLCJuYW1lIjoiTGVuIiwiaWF0IjoxNTczMDI4MjU2fQ ,以在视图模型更改时启动小吃栏。例如:

StatelessWidget

只有在所有小部件都构建好后,我们才能显示小吃店,这就是我们在上面进行class SnackBarLauncher extends StatelessWidget { final String error; const SnackBarLauncher( {Key key, @required this.error}) : super(key: key); @override Widget build(BuildContext context) { if (error != null) { WidgetsBinding.instance.addPostFrameCallback( (_) => _displaySnackBar(context, error: error)); } // Placeholder container widget return Container(); } void _displaySnackBar(BuildContext context, {@required String error}) { final snackBar = SnackBar(content: Text(error)); Scaffold.of(context).hideCurrentSnackBar(); Scaffold.of(context).showSnackBar(snackBar); } } 调用的原因。

现在我们可以在屏幕上添加WidgetsBinding.instance.addPostFrameCallback()

SnackBarLauncher