firebase - 在 Flutter StateNotifier + Riverpod 架构中初始化未来数据
问题描述
所以我在这篇文章中实现了 ResoCoder 在 YouTube 上展示的架构:https ://resocoder.com/2020/12/11/flutter-statenotifier-riverpod-tutorial-immutable-state-management/#t-1607415476843 。
我在理解什么是按照他的方案初始化未来数据的最佳方法时遇到了一些问题。
假设我有一个虚拟的 Repository 类:
abstract class WeatherRepository {
Future<Weather> fetchWeather(String cityName);
}
class FakeWeatherRepository implements WeatherRepository {
double cachedTempCelsius;
@override
Future<Weather> fetchWeather(String cityName) {
// Simulate network delay
return Future.delayed(
Duration(seconds: 1),
() {
final random = Random();
// Simulate some network exception
if (random.nextBool()) {
throw NetworkException();
}
// Since we're inside a fake repository, we need to cache the temperature
// in order to have the same one returned in for the detailed weather
cachedTempCelsius = 20 + random.nextInt(15) + random.nextDouble();
// Return "fetched" weather
return Weather(
cityName: cityName,
// Temperature between 20 and 35.99
temperatureCelsius: cachedTempCelsius,
);
},
);
}
}
class NetworkException implements Exception {}
天气状态类:
abstract class WeatherState {
const WeatherState();
}
class WeatherInitial extends WeatherState {
const WeatherInitial();
}
class WeatherLoading extends WeatherState {
const WeatherLoading();
}
class WeatherLoaded extends WeatherState {
final Weather weather;
const WeatherLoaded(this.weather);
}
class WeatherError extends WeatherState {
final String message;
const WeatherError(this.message);
}
天气通知器:
class WeatherNotifier extends StateNotifier<WeatherState> {
final WeatherRepository _weatherRepository;
WeatherNotifier(this._weatherRepository) : super(WeatherInitial());
Future<void> getWeather(String cityName) async {
try {
state = WeatherLoading();
final weather = await _weatherRepository.fetchWeather(cityName);
state = WeatherLoaded(weather);
} on NetworkException {
state = WeatherError("Couldn't fetch weather. Is the device online?");
}
}
}
两家供应商:
final weatherRepositoryProvider = Provider<WeatherRepository>(
(ref) => FakeWeatherRepository(),
);
final weatherNotifierProvider = StateNotifierProvider(
(ref) => WeatherNotifier(ref.watch(weatherRepositoryProvider)),
);
和 WeatherSearchPage (UI):
...
child: Consumer(
builder: (context, watch, child) {
final state = watch(weatherNotifierProvider.state);
if (state is WeatherInitial) {
return buildInitialInput();
} else if (state is WeatherLoading) {
return buildLoading();
} else if (state is WeatherLoaded) {
return buildColumnWithData(state.weather);
} else {
// (state is WeatherError)
return buildInitialInput();
}
},
),
...
在哪里
Widget buildInitialInput() {
return Center(
child: CityInputField(), // builds a textfield to fetch the weather of some city
);
}
和
Widget buildLoading() {
return Center(
child: CircularProgressIndicator(),
);
}
Column buildColumnWithData(Weather weather) {
return Column(
...
//shows data
...
);
}
如果我需要在创建页面时获取默认城市天气,我应该按照这个逻辑在哪里调用它?
我尝试将 WeatherSearchPage (UI) 转换为有状态小部件并像这样调用 initState
@override
void initState() {
context.read(weatherNotifierProvider).getWeather("Siena");
super.initState();
}
它有效,但看起来不是很干净,并且没有利用小部件的 InitialState。有什么建议么?
谢谢!
解决方案
首先,在这里使用 StateNotifier 没有多大意义。这并没有错,但我想我可以向您展示一种更简单的方法来做到这一点,它真正利用了 Riverpod 所提供的优势。
您可以使用FutureProvider并使用AsyncValue状态包装组件的内容。
class FakeWeatherRepository implements WeatherRepository {
double cachedTempCelsius;
static final provider = Provider<FakeWeatherRepository>((_) => FakeWeatherRepository());
@override
Future<Weather> fetchWeather(String cityName) async {
// Get weather
}
}
final weatherProvider = FutureProvider.family<Weather, String>((ref, city) {
final repo = ref.watch(FakeWeatherRepository.provider);
return repo.fetchWeather(city);
});
class WeatherWidget extends ConsumerWidget {
const WeatherWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context, ScopedReader watch) {
return Scaffold(
body: watch(weatherProvider('Siena')).when(
data: (weather) => Center(
child: Text(weather),
),
loading: () => Center(
child: const CircularProgressIndicator(),
),
error: (err, stack) => Center(
child: Text(err.toString()),
),
),
);
}
}
然后您当然可以使用一些状态提供程序或 ValueNotifier 等来更改您传递给weatherProvider
.
我希望这会有所帮助,我知道我没有完全回答您的问题,但如果没有 AsyncValue 的力量,我不能让您继续您的 Riverpod 之旅!
推荐阅读
- windows - 如何在 windows powershell/cmd 中为命令输入文件数据
- sql - SSMS 数据导入数据被截断错误消息
- reactjs - React-Datepicker 布局在 Bootstrap-React 模式中被破坏(日历被截断)
- javascript - 如何在切换选项卡中加载 Javascript?
- javascript - 使用 javascript 启用/禁用提交按钮 - 根据用户在 HTML 中输入的所有数字的总和
- c# - ASP.NET MVC 禁用按钮
- python - 安装 numpy 版本 1.16.5
- next.js - 重定向到 nextjs 中的规范 url
- php - 在 Imagick (PHP) 中,我如何在不更改 alpha(透明)的情况下进行否定?
- python - 使用 Python 通过 API 获取 DHL 和 Hermes Shipments 的状态