Как подключить локализацию L10n c Riverpod без ошибок с HTTP Requests
Статья для начинающих в Riverpod
До этого пользовался Provider совместно с BLoC и недавно решился попробовать Riverpod в одном из проектов. В ходе работы столкнулся с проблемой, которую многие могут не замечать.
При использовании Logger для Http запросов он отправлял несколько запросов, даже если был отправлен лишь 1. Для устранения проблемы нужно будет создать собственный провайдер для Dio и работать уже с ним.
Первым делом: создаем localeProvider
final localeProvider = StateNotifierProvider<LocaleNotifier, Locale>((ref) {
return LocaleNotifier();
});
class LocaleNotifier extends StateNotifier<Locale> {
LocaleNotifier() : super(sharedPreference.locale) {
onAppStart();
}
void changeLanguage(Locale locale) {
try {
if (!L10n.all.contains(locale)) return;
state = locale;
} catch (error) {
state = sharedPreference.locale;
}
}
void onAppStart() {
try {
final locale = sharedPreference.locale;
state = locale;
} catch (error) {
state = const Locale('ru');
}
}
}
2. Соединяем его с MaterialApp
Обертываем наш MyApp в ProviderScope() и через метод watch "слушаем" изменение языка. И добавляем ProviderContainer в нашем main.dart файле.final ProviderContainer container = ProviderContainer();
runApp(
ProviderScope(
child: WorkspaceApp(),
),
);
Widget build(BuildContext context, WidgetRef ref) {
final locale = ref.watch(localeProvider);
return MaterialApp.router(
title: "Workspace",
locale: locale,
routerConfig: _router,
supportedLocales: L10n.all,
theme: LightTheme,
darkTheme: null,
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.light,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
builder: EasyLoading.init(),
);
}
3. HttpQuery для get запроса
Создаем http_query.dart файл и вставляем код ниже:
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logger/logger.dart';
import 'package:pretty_dio_logger/pretty_dio_logger.dart';
class HttpQuery {
final dioProvider = Provider((ref) {
PrettyDioLogger logger = PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseBody: false,
responseHeader: true,
error: true,
compact: true,
maxWidth: 100,
);
Dio dio = Dio(BaseOptions(baseUrl: '${sharedPreference.chosenServer}/api/'));
dio.interceptors.add(ErrorInterceptor());
dio.interceptors.add(logger);
return dio;
});
/* ---------------------------------- HttpQuery ---------------------------------- */
Future<dynamic> get(
{required String url, Map<String, dynamic>? queryParameters, Map<String, dynamic>? headerData}) async {
try {
container.read(dioProvider).interceptors.add(ErrorInterceptor());
container.read(dioProvider).interceptors.add(logger);
Map<String, dynamic> header = {
"Content-Type": "application/json",
};
Map<String, dynamic> queryParameters1 = queryParameters ?? {};
String? token = await UserSecureStorage.getToken();
if (token != "") header.addAll({"Authorization": 'Bearer $token'});
if (headerData != null) header.addAll(headerData);
final Response result = await container.read(dioProvider).get(
url,
options: Options(
sendTimeout: 30000,
receiveTimeout: 60000,
headers: header,
),
queryParameters: queryParameters1,
);
return result.data;
} on DioError catch (error) {
if (error.type == DioErrorType.connectTimeout) {
debugPrint(error.type.name);
}
if (error.type == DioErrorType.receiveTimeout) {
debugPrint(error.type.name);
}
return error;
}
}
}
Наш dioProvider мы можем использовать за место getIt. В нем описываем базовые параметры для Dio, в моем случае я вставил туда базовое начало моих url запросов.
И создаем providerContainer для доступа к провайдера без WidgetRef. Далее для получения результата Response мы отправляем запрос через наш dioProvider получая доступ к нему через наш providerContainer.
4. Изменение языка
Используем вместо StatelessWidget - ConsumerWidget
class LanguagePickerWidget extends ConsumerWidget {
const LanguagePickerWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final locale = ref.read(localeProvider);
return DropdownButtonHideUnderline(
child: DropdownButton(
value: locale,
icon: Container(width: 12),
items: L10n.all.map(
(locale) {
final flag = L10n.getFlag(locale.languageCode);
return DropdownMenuItem(
value: locale,
onTap: () {
ref.read(localeProvider.notifier).changeLanguage(locale);
},
child: Center(
child: Text(
flag,
style: const TextStyle(fontSize: 32),
),
),
);
},
).toList(),
onChanged: (_) {},
),
);
}
}
Получаем доступ к нашему localeProvider и через метод notifier, который получает уведомления о изменении состоянии мы меняем наш язык.