Http 颤振:如何从不是小部件的类中调用SnackBar

Http 颤振:如何从不是小部件的类中调用SnackBar,http,flutter,interceptor,snackbar,Http,Flutter,Interceptor,Snackbar,我从Flatter开始,制作了一个简单的应用程序,使用RESTAPI管理登录屏幕 我使用http包和包截获并发送头中的令牌 问题是。。。我可以用拦截器捕获错误,没有任何问题。但是,有没有办法使用一个全局snackbar,它可以从我的拦截器类“通知”并将用户重定向到显示应用程序中任何错误的登录屏幕,例如,当令牌无效时 这是我的拦截器类: class ApiInterceptor with ChangeNotifier implements InterceptorContract { final

我从Flatter开始,制作了一个简单的应用程序,使用RESTAPI管理登录屏幕

我使用http包和包截获并发送头中的令牌

问题是。。。我可以用拦截器捕获错误,没有任何问题。但是,有没有办法使用一个全局snackbar,它可以从我的拦截器类“通知”并将用户重定向到显示应用程序中任何错误的登录屏幕,例如,当令牌无效时

这是我的拦截器类:

class ApiInterceptor with ChangeNotifier implements InterceptorContract {
  final storage = new FlutterSecureStorage();
  @override
  Future<RequestData> interceptRequest({RequestData data}) async {
    [...] // here is the request interceptor
    return data;
  }

  // The response interceptor:
  @override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    final decodedResponse = json.decode(data.body);
    if (data.statusCode >= 400) {
      throw HttpException(decodedResponse['error']);
      // here i want to send the notification to a snackBar
      // then, i want to redirect the user to the login screen
    }
    return data;
  }
}


使用
MultipleProvider
widget在my main.dart上调用这些提供程序:

@override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider.value(
          value: ApiInterceptor(),
        ),
        ChangeNotifierProvider.value(
          value: Auth(),
        ),
        ChangeNotifierProvider.value(
          value: TurnActive(),
        ),
      ],
      child: MaterialApp(
.
.
.
[UPDATE II]

这是更新后的
main.dart
。。。但仍然不起作用

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  final storage = new FlutterSecureStorage();
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'CAD App',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        body: MultiProvider(
          providers: [
            ChangeNotifierProvider.value(
              value: ApiInterceptor(context: context),
            ),
            ChangeNotifierProvider.value(
              value: Auth(context: context),
            ),
            ChangeNotifierProvider.value(
              value: TurnActive(context: context),
            ),
          ],
          child: FutureBuilder(
            future: storage.read(key: "token"),
            builder: (context, storedKey) {
              if (!storedKey.hasData) {
                return LoadingData(text: 'Por favor espere...');
              } else {
                return storedKey.data == null
                    ? LoginPage()
                    : InitialLoadingPage();
              }
            },
          ),
        ),
      ),
    );
  }
}

在我的拦截器上:

.
.
.
@override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    final decodedResponse = json.decode(data.body);

    Scaffold.of(context).showSnackBar(SnackBar(
      content: Text(decodedResponse['error']),
    ));
.
.
.
。
.
.
@凌驾
未来侦听响应({ResponseData})异步{
final decodedResponse=json.decode(data.body);
Scaffold.of(上下文).showSnackBar(SnackBar(
内容:文本(decodedResponse['error']),
));
.
.
.
错误是:
Scaffold.of()使用不包含Scaffold的上下文调用。

最新答案 最初的答案将
BuildContext
传递到您的
ChangeNotifier
服务中,该服务在技术上是有效的,但在查看后,我意识到它非常不专业。这是因为使用
提供者
或服务的整个概念是将小部件构建和后台功能分开。传递
BuildContext
和从服务内部创建一个
Snackbar
并不是很好。Bellow是一个更专业、稍微多一些的工作,可以让你的头脑围绕它,但从长远来看更灵活

想法 因此,所有
小部件
代码都包含在您用于UI和UX的类中,您需要在类中具有某种类型的函数,但只能从
APInterceptor
调用。为此,您将使用可应用于变量的所谓函数

步骤1:创建
typedef
您的
typedef
应该在类之外创建,但仍然在要应用它的主文件中,最好是在包含
apinterceptor
的文件中

typedef void OnInterceptError(字符串错误消息);
如果您从未在任何语言中使用过
typedef
,您可能会感到非常困惑。您所做的只是创建一个函数类型,它返回
void
,并以
字符串
作为输入

步骤2:在
apinterceptor内使用
OnInterceptError
apinterceptor({
@此错误是必需的,
}):assert(interceptError!=null);
最终OnInterceptorThis.interceptError;
//响应拦截器
@凌驾
未来侦听响应({ResponseData})异步{
final decodedResponse=json.decode(data.body);
如果(data.statusCode>=400){
抛出HttpException(decodedResponse['error']);
//运行“interceptError”将通知发送到
//“小吃吧`
截取错误(decodedResponse['error']);
}
返回数据;
}
设置好这个之后,你终于可以进入好的部分了:设置UI

步骤3:创建
onInterceptorror
函数。。。 现在您已经知道了函数的运行位置,您需要创建函数具有其…功能的位置

无论您在何处实现此
apinterceptor
服务,现在都应该传递以下内容

apinterceptor(
interceptError:(字符串errorMessage){
//从这里显示“Snackbar”,它应该有
//访问“BuildContext”以执行此操作并使用
//`interceptError`来为
//“Snackbar”,如果你愿意的话。
打印(错误);
}
);
一开始它看起来非常复杂,但它确实是一种很好的方法,因为它将您的服务和UI分开

原始答案 不幸的是,由于Dart的工作原理,抓取
BuildContext
可能有点麻烦,但100%可能。我将引导您完成以下步骤:

步骤1:在
apinterceptor
当前,您的
apinterceptor
类被声明为没有任何输入变量,因此您将在顶部向类添加以下内容

apinterceptor({
@在这种情况下,
}):assert(上下文!=null);
最终构建语境;
现在,每次在代码库中访问类时,IDE都会通知您缺少一个变量

步骤2:在
Auth
遗憾的是,您必须对
Auth
提供程序执行完全相同的操作。我将为您保留与上一步相同的独白,因为它们几乎是相同的过程。以下是您必须添加到
Auth
类开头的内容

Auth({
@在这种情况下,
}):assert(上下文!=null);
最终构建语境;
步骤3:在每个必需的情况下传递
BuildContext

您可能会发现,IDE为您完成了大部分工作!以下是所有类的完整代码

带有ChangeNotifier的类APInterceptor实现InterceptorContract{
API拦截器({
@在这种情况下,
}):assert(上下文!=null);
最终构建语境;
最终存储=新存储();
@凌驾
未来拦截请求({RequestData})异步{
[…]//这是请求拦截器
返回数据;
}
//响应拦截器:
@凌驾
未来侦听响应({ResponseData})异步{
final decodedResponse=json.d
.
.
.
@override
  Future<ResponseData> interceptResponse({ResponseData data}) async {
    final decodedResponse = json.decode(data.body);

    Scaffold.of(context).showSnackBar(SnackBar(
      content: Text(decodedResponse['error']),
    ));
.
.
.