【问题标题】:How to make a Sink<Locale> to format the result of a Stream<String>?如何使 Sink<Locale> 格式化 Stream<String> 的结果?
【发布时间】:2019-02-19 19:07:23
【问题描述】:

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

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

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

【问题讨论】:

  • 我推荐你看这个演讲skillsmatter.com/skillscasts/…
  • 我不太确定您是否希望在 BLoC 中使用它。 Locale 是一个 Flutter 类。不是飞镖的。你会失去网络兼容性(或者需要分叉类)
  • 网络兼容性是什么意思? Flutter 仅适用于移动设备。
  • 飞镖不是。而本次演讲中使用的技术(BLoC)用于增强 Flutter 和 Web 之间的代码共享,例如 angulardart

标签: dart flutter googleio


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    做这个跨平台的最佳人选是来自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)),
            )
          ],
        );
      }
    }
    

    【讨论】:

    • 我认为这个答案严重超载。
    • 只需要CurrencyBloc,并且应该包含CurrencyProvider,因为它遵循约定。我可能会摆脱其他的东西。
    • @creativecreatorormaybenot 我简化了我的答案以排除不必要的类。
    • 在currency_bloc.dart 中有_currencyController.stream.distinct()。我相信distinct() 不一定在这里工作,因为LocalCurrency 类没有定义== 运算符。
    • 好收获。我添加了覆盖。
    猜你喜欢
    • 1970-01-01
    • 2020-08-08
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2023-03-31
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    相关资源
    最近更新 更多