Обновленный ответ
Оригинальный ответ передает BuildContext
в ваш ChangeNotifier
сервис, который технически работает, но после просмотра я понял, что это довольно непрофессионально. Это связано с тем, что вся концепция использования Provider
или службы заключается в том, чтобы отделить здание виджета от фоновых функций. Проходить BuildContext
и создавать Snackbar
изнутри сервиса не очень хорошо. Беллоу гораздо более профессиональный, немного более трудоемкий, чтобы обернуть вокруг него голову, но гораздо более гибкий в долгосрочной перспективе.
Идея
Так что весь код Widget
содержится внутри класса, который вы используете для пользовательского интерфейса и UX, вам нужно иметь некоторый тип функции, который есть в классе, но вызывается только из вашего ApiInterceptor
. Для этого вы будете использовать то, что называется typedef
, которое может быть применено к переменной.
Шаг 1: Создайте typedef
Ваш typedef
должен быть создан вне класса, но все же в основном файле, который вы собираетесь применять, желательно в файле, содержащем ApiInterceptor
.
typedef void OnInterceptError (String errorMessage);
Если вы никогда не работали с typedef
на любом языке вы, вероятно, очень запутались. Все это делает создание типа функции, которая возвращает void
и принимает String
для ввода.
Шаг 2: Использование OnInterceptError
внутри ApiInterceptor
ApiInterceptor({
@required this.interceptError,
}) : assert(interceptError != null);
final OnInterceptError this.interceptError;
// Response interceptor
@override
Future<ResponseData> interceptResponse({ResponseData data}) async {
final decodedResponse = json.decode(data.body);
if (data.statusCode >= 400) {
throw HttpException(decodedResponse['error']);
// Run `interceptError` to send the notification to a
// `Snackbar`
interceptError(decodedResponse['error']);
}
return data;
}
После настройки вы, наконец, можете приступить к хорошему: настройте пользовательский интерфейс !!!
Шаг 3: Создайте функцию OnInterceptError
...
Теперь, когда у вас есть где функция запускается, вам нужно создать, где функция имеет свою ... функциональность .
Где бы вы ни внедрили эту службу ApiInterceptor
, теперь вы должны передать что-то с эффектом следующий.
ApiInterceptor(
interceptError: (String errorMessage) {
// Show the `Snackbar` from here, which should have
// access to the `BuildContext` to do so and use
// `interceptError` to create the message for the
// `Snackbar`, if you'd like to do so.
print(interceptError);
}
);
Сначала это кажется действительно очень сложным, но это действительно хороший способ сделать что-то, потому что он разделяет ваши сервисы и пользовательский интерфейс. Ниже приведен оригинальный ответ, если вам нужна ссылка или вы все еще хотите использовать этот метод.
Оригинальный ответ
К сожалению, из-за того, как Дарт работает, захват 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 MaterialApp(
home: Scaffold(
home: Builder(
builder: (BuildContext context) => MultiProvider(
providers: [
ChangeNotifierProvider.value(
value: ApiInterceptor(context: context),
),
ChangeNotifierProvider.value(
value: Auth(context: context),
),
ChangeNotifierProvider.value(
value: TurnActive(),
),
],
child: /* CHILD!!! */,
),
),
),
);
}
Убедитесь, что Builder
находится под Scaffold
в дереве, иначе он не узнает Scaffold
при вызове Scaffold.of(context)
.
Надеюсь, это поможет и сделает ваш день на немного проще.