如何使用Flutter中的Provider显示ChangeNotificationer的错误



我正试图找到通过Snackbar显示带有Provider的变更通知程序模型中的错误的最佳方式。

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

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

假设我有一个简单的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();
}
}
}

谢谢

编辑2022

如果有人对感兴趣,我也为river pod移植(并重新设计(了这个包

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

编辑2020-06-05

我开发了一种稍微好一点的方法来应对这种情况。

它可以在github上的This repo中找到,这样你就可以在那里看到实现,或者在你的pubspec.yaml 中使用这个包

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

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

  1. MessageNotifierMixin扩展ChangeNotifier,使ChangeNotifier具有两个属性errorinfo,以及两个方法notifyError()notifyInfo()

  2. 用MessageListener包裹你的Scaffold,当它被调用notifyError((或NotifyInfo((时,它将显示Snackbar

我给你举个例子:

变更通知程序

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

查看

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])
),
),
)
)
);
}
}

旧答案

谢谢。

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

使用自定义的无状态小部件(我称之为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()
)
)
);
}

您可以创建一个自定义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);
}
}

只有在构建完所有小部件后,我们才能显示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),
),
],
),
);
}
}

相关内容

  • 没有找到相关文章

最新更新