如何制作接收器<Locale>以格式化流的结果<String>?



在谷歌IO 18中,Flutter演示者展示了一个功能,但没有展示如何实现它。 视频(在确切时间(是:https://youtu.be/RS36gBEp8OI?t=1776

如何实现这样的事情?如何根据接收器正确格式化流?

(对不起,我对Rx不太熟悉(

使用 rxdart 包中的combineLatest函数。它采用输入流的最新值,因此每当区域设置或购物车项目更改时,它都会计算并格式化总成本。

import 'dart:async'; // Sink, Stream
import 'dart:ui'; // Locale
import 'package:rxdart/rxdart.dart'; // Observable, *Subject
class Bloc {
var _locale = BehaviorSubject<Locale>(seedValue: Locale('en', 'US'));
var _items = BehaviorSubject<List<CartItem>>(seedValue: []);
Stream<String> _totalCost;
Sink<Locale> get locale => _locale.sink;
Stream<List<CartItem>> get items => _items.stream;
Stream<String> get totalCost => _totalCost;
Bloc() {
_totalCost = Observable.combineLatest2<Locale, List<CartItem>, String>(
_locale, _items, (locale, items) {
// TODO calculate total price of items and format based on locale
return 'USD 10.00';
}).asBroadcastStream();
}
void dispose() {
_locale.close();
_items.close();
}
}

免责声明:我没有尝试运行此代码,因此可能存在错误,但基本思想应该是可靠的。

执行此跨平台操作的最佳候选者是intl包中的 NumberFormat。但是,您仍然必须向其传递区域设置字符串("en_US"(和ISO 4217货币代码("USD"(。

经过一番挖掘,我无法在任何Dart包中找到此信息。NumberFormat类有一个私有地图,用于从货币代码中查找货币符号("$"(,但映射的键(货币代码(无法访问。因此,我决定制作一个使区域设置字符串和货币代码可用的包。

currency_bloc.飞镖

import 'dart:async';
import 'package:rxdart/rxdart.dart';
import 'package:intl/intl.dart';
import 'package:locales/locales.dart';
import 'package:locales/currency_codes.dart';
class LocalCurrency {
const LocalCurrency(this.locale, this.code);
final Locale locale;
final CurrencyCode code;
@override toString() => '$code ($locale)';
@override operator==(o) => o is LocalCurrency && o.locale == locale && o.code == code;
@override hashCode => toString().hashCode;
}
/// Emits currency strings according to a locale.
class CurrencyBloc {
// Inputs.
final _valueController = StreamController<double>();
final _currencyController = StreamController<LocalCurrency>();
// Outputs.
final _currency = BehaviorSubject<String>();
/// The last formatted currency value emitted from the output stream.
String lastCurrency;
// For synchronously receiving the latest inputs.
double _value;
NumberFormat _formatter;
CurrencyBloc({LocalCurrency initialCurrency, double initialValue}) {
_valueController.stream
.distinct()
.listen((value) => _updateCurrency(value: value));
_currencyController.stream
.distinct()
.listen((currency) => _updateCurrency(currency: currency));
// Initialize inputs.
locale.add(initialCurrency ??
LocalCurrency(Locale.en_US, CurrencyCode.usd));
value.add(initialValue ?? 0.0);
}
void dispose() {
_valueController.close();
_currencyController.close();
_currency.close();
}
_updateCurrency({double value, LocalCurrency currency}) {
if (currency != null) {
_formatter = NumberFormat.simpleCurrency(
locale: '${currency.locale}',
name: '${currency.code}',
decimalDigits: 2);
}
if (value != null) {
_value = value;
}
if (_value != null && _formatter != null) {
lastCurrency = _formatter.format(_value);
_currency.add(lastCurrency);
}
}
/// Change the current [Locale] and/or [CurrencyCode].
Sink<LocalCurrency> get locale => _currencyController.sink;
/// Change the the value to be formatted.
Sink<double> get value => _valueController.sink;
/// Formatted currency.
Stream<String> get currency => _currency.stream;
}

currency_provider.飞镖(常规(

class CurrencyProvider extends InheritedWidget {
CurrencyProvider({Key key, @required this.bloc, @required Widget child})
: super(key: key, child: child);
final CurrencyBloc bloc;
@override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static CurrencyBloc of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(CurrencyProvider) as CurrencyProvider)
.bloc;
}

示例用法

...
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CurrencyBloc bloc;
@override
Widget build(BuildContext context) =>
CurrencyProvider(bloc: bloc, child: CurrencyExample());
@override
void initState() {
super.initState();
bloc = CurrencyBloc();
}
@override
void dispose() {
bloc.dispose();
super.dispose();
}
@override
void didUpdateWidget(StatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
bloc.dispose();
bloc = CurrencyBloc();
}
}
class CurrencyExample extends StatelessWidget {
final controller = TextEditingController();
@override
Widget build(BuildContext context) {
final bloc = CurrencyProvider.of(context);
return ListView(
children: <Widget>[
TextField(controller: controller),
StreamBuilder(
stream: bloc.currency,
initialData: bloc.lastCurrency,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data);
} else if (snapshot.hasError) {
return new Text('${snapshot.error}');
}
return Center(child: CircularProgressIndicator());
}),
FlatButton(
child: Text('Format Currency'),
onPressed: () => bloc.value.add(double.tryParse(controller.text)),
)
],
);
}
}

最新更新