Flutter:如何从不是窗口小部件的类中调用SnackBar

问题描述 投票:0回答:1

我从颤抖开始,我制作了一个简单的应用程序,该应用程序使用REST API管理登录屏幕。

我正在使用http包和http_interceptor包来拦截并发送标头中的令牌。

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

这是我的拦截器类:

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


[UPDATE I]

这里是我使用的提供者。在此提供程序中,我使用拦截器。

import 'dart:convert';

import 'package:cadsanjuan_movil/models/http_exception.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart';
import 'package:http_interceptor/http_interceptor.dart';
import '../config/http_interceptor.dart';
import '../config/.env.dart' as config;

class Auth with ChangeNotifier {
  String _endpoint = 'auth';

  final storage = new FlutterSecureStorage();
  // Http Interceptor
  Client http = HttpClientWithInterceptor.build(interceptors: [
    ApiInterceptor(),
  ]);

  Future singup(String email, String password) async {
    final url = "${config.apiBaseUrl}/$_endpoint/signin";
    try {
      final response = await http.post(url,
          body: json.encode({'email': email, 'password': password}));
      final decodedResponse = json.decode(response.body);
/*       if (response.statusCode >= 400) {
        throw HttpException(decodedResponse['error']);
      } */
      await storage.write(key: 'token', value: decodedResponse['token']);
      await storage.write(key: 'user', value: decodedResponse['user']);
      await storage.write(key: 'email', value: decodedResponse['email']);
      await storage.write(
          key: 'employeeId', value: decodedResponse['employeeId'].toString());
      //notifyListeners();
    } catch (error) {
      throw error;
    }
  }
}

这些提供者在我的main.dart中使用MultipleProvider小部件被调用:

@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']),
    ));
.
.
.

错误是:Scaffold.of() called with a context that does not contain a Scaffold.

error

http flutter interceptor snackbar
1个回答
0
投票

[由于Dart的工作原理,抓住BuildContext有点可惜,但100%的可能性。我将指导您完成以下步骤:

步骤1:在BuildContext中需要ApiInterceptor

当前正在声明ApiInterceptor类,而没有任何输入变量,因此您需要在顶部的类中添加以下内容。

ApiInterceptor({
  @required this.context,
}) : assert(context != null);

final BuildContext context;

现在,每次在代码库中访问您的类时,IDE都会通知您缺少变量。

步骤2:要求BuildContext中的Auth

您很遗憾必须与Auth提供者做同样的事情。我将为您保留与上一步相同的独白,因为它们几乎是相同的过程。以下是您必须添加到Auth类的开头的内容。

Auth({
  @required this.context,
}) : assert(context != null);

final BuildContext context;

步骤3:在每种情况下都通过BuildContext

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

class ApiInterceptor with ChangeNotifier implements InterceptorContract {
  ApiInterceptor({
    @required this.context,
  }) : assert(context != null);

  final BuildContext context;
  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;
  }
}

class Auth with ChangeNotifier {
  Auth({
    @required this.context,
  }) : assert(context != null);

  final BuildContext context;

  String _endpoint = 'auth';

  final storage = new FlutterSecureStorage();

  Future singup(String email, String password) async {
    // Http Interceptor
    Client http = HttpClientWithInterceptor.build(interceptors: [
      ApiInterceptor(context: context),
    ]);
    final url = "${config.apiBaseUrl}/$_endpoint/signin";
    try {
      final response = await http.post(url,
          body: json.encode({'email': email, 'password': password}));
      final decodedResponse = json.decode(response.body);
/*       if (response.statusCode >= 400) {
        throw HttpException(decodedResponse['error']);
      } */
      await storage.write(key: 'token', value: decodedResponse['token']);
      await storage.write(key: 'user', value: decodedResponse['user']);
      await storage.write(key: 'email', value: decodedResponse['email']);
      await storage.write(
          key: 'employeeId', value: decodedResponse['employeeId'].toString());
      //notifyListeners();
    } catch (error) {
      throw error;
    }
  }
}

当然,您的main()输出:

@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider.value(
        value: ApiInterceptor(context: context),
      ),
      ChangeNotifierProvider.value(
        value: Auth(context: context),
      ),
      ChangeNotifierProvider.value(
        value: TurnActive(),
      ),
    ],
    child: /* CHILD!!! */,
  );
}

我希望这会有所帮助,并使您的一天变得[[小更加轻松。

© www.soinside.com 2019 - 2024. All rights reserved.