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

问题描述 投票:2回答:2

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

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

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

假设我有一个简单的页面,我想在其中显示对象列表,而一个模型是我从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();
    }
  }

}

谢谢

flutter dart flutter-layout flutter-provider provider-model
2个回答
0
投票

您可以创建自定义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

class SomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Title',
        ),
      ),
      body: Stack(
        children: [
          // Other widgets here...

          Consumer<EmailLoginScreenModel>(
            builder: (context, model, child) =>
                SnackBarLauncher(error: model.error),
          ),
        ],
      ),
    );
  }
}

0
投票

谢谢。

也许我发现了一种更简单的方法,即使用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();
  }
}

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

我在这里使用混合来确保模型具有error属性和clarError()方法。

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

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

  void clearError() {
    _error = null;
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.