在google IO 18中,Flutter演示者展示了一项功能,但未展示如何实现此功能。 视频(确切时间)为:https://youtu.be/RS36gBEp8OI?t=1776
该如何实施?如何根据接收器正确设置Stream的正确格式?
(对不起,但我对Rx不太熟悉)
答案 0 :(得分:2)
使用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();
}
}
免责声明:我并未尝试运行此代码,因此可能会出现错误,但基本思路应该牢固。
答案 1 :(得分:0)
执行此跨平台的最佳人选是intl
软件包中的NumberFormat。但是,您仍然必须向其传递语言环境字符串(“ en_US
”)和ISO 4217货币代码(“ USD
”)。
经过一番挖掘,我在任何Dart程序包中都找不到此信息。 NumberFormat
类具有用于从货币代码中查找货币符号(“ $
”)的私有地图,但是无法访问该地图的键(即货币代码)。因此,我决定制作一个package,使其可以使用区域设置字符串和货币代码。
currency_bloc.dart
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.dart (常规)
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)),
)
],
);
}
}