4

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

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

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

4

2 回答 2

2

使用combineLatestrxdart 包中的函数。它采用输入流的最新值,因此只要语言环境或购物车项目发生变化,它就会计算并格式化总成本。

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();
  }
}

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

于 2018-09-22T14:09:36.470 回答
0

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

经过一番挖掘,我在任何 Dart 包中都找不到此信息。该类NumberFormat有一个私有映射,用于从货币代码中查找货币符号(“ $”),但映射的键,即货币代码,是不可访问的。所以我决定制作一个,使语言环境字符串和货币代码可用。

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)),
        )
      ],
    );
  }
}
于 2018-09-22T20:28:03.077 回答