响应式编程是一种以对数据随时间变化做出反应为中心的范式。它有助于自动传播更新,从而可确保 UI 和数据保持同步。用 Flutter 术语来说,这意味着只要状态发生变化就会自动触发重建。
Observables
Observables是响应式编程的核心。这些数据源会在数据发生变化时向订阅者发出更新。Dart 的核心可观察类型是 Stream。
当状态发生变化时,可观察对象会通知侦听器。从用户交互到数据获取操作的任何事情都可以触发此操作。这有助于 Flutter 应用程序实时响应用户输入和其他更改。
Flutter 有两种类型:ValueNotifier和ChangeNotifier,它们是类似 observable 的类型,但不提供任何真正的可组合性计算。
ValueNotifier
Flutter 中的类ValueNotifier在某种意义上是响应式的,因为当值发生变化时它会通知观察者,但您需要手动监听所有值的变化来计算完整的值。
1、监听
            
            
              ini
              
              
            
          
            // 初始化
 final ValueNotifier<String> fristName = ValueNotifier('Tom');
 final ValueNotifier<String> secondName = ValueNotifier('Joy');
 late final ValueNotifier<String> fullName;
  @override
  void initState() {
    super.initState();
    fullName = ValueNotifier('${fristName.value} ${secondName.value}');
    fristName.addListener(_updateFullName);
    secondName.addListener(_updateFullName);
  }
  
  void _updateFullName() {
    fullName.value = '${fristName.value} ${secondName.value}';
  }
  //更改值得时候
  firstName.value = 'Jane'
  secondName.value = 'Jane'
        2、使用ValueListenableBuilder更新UI
            
            
              less
              
              
            
          
           //通知观察者
 ValueListenableBuilder<String>(
   valueListenable: fullName,
   builder: (context, value, child) => Text(
      '${fristName.value} ${secondName.value}',
       style: Theme.of(context).textTheme.headlineMedium,
   ),
),
        ChangeNotifier
1、监听
            
            
              ini
              
              
            
          
            String _firstName = 'Jane';
  String _secondName = 'Doe';
  String get firstName => _firstName;
  String get secondName => _secondName;
  String get fullName => '$_firstName $_secondName';
  set firstName(String newName) {
    if (newName != _firstName) {
      _firstName = newName;
      // Triggers rebuild
      notifyListeners();
    }
  }
  set secondName(String newSecondName) {
    if (newSecondName != _secondName) {
      _secondName = newSecondName;
      // Triggers rebuild
      notifyListeners();
    }
  }
  
  //更改值得时候
  firstName.value = 'Jane'
  secondName.value = 'Jane'
        2、使用AnimatedBuilder更新UI
            
            
              less
              
              
            
          
          //通知观察者
AnimatedBuilder(
     animation: fullName,
     builder: (context, child) => Text(
     fullName,
     style: Theme.of(context).textTheme.headlineMedium,
    ),
 ),
        get
GetX将响应式编程变得非常简单。
- 您不需要创建 StreamController。
 - 您不需要为每个变量创建一个 StreamBuilder。
 - 你不需要为每个状态创建一个类。
 - 你不需要创造一个终极价值。
 
使用 Get 的响应式编程就像使用 setState 一样简单。 让我们想象一下,您有一个名称变量,并且希望每次更改它时,所有使用它的小组件都会自动刷新。
1、监听以及更新UI
            
            
              ini
              
              
            
          
          //这是一个普通的字符串
var name = 'Jonatas Borges';
为了使观察变得更加可观察,你只需要在它的附加上添加".obs"。
var name = 'Jonatas Borges'.obs;
而在UI中,当你想显示该值并在值变化时更新页面时,只需这样做。
Obx(() => Text("${controller.name}"));
        Riverpod
            
            
              ini
              
              
            
          
          final fristNameProvider = StateProvider<String>((ref) => 'Tom');
final secondNameProvider = StateProvider<String>((ref) => 'Joy');
final fullNameProvider = StateProvider<String>((ref) {
  final fristName = ref.watch(fristNameProvider);
  final secondName = ref.watch(secondNameProvider);
  return '$fristName  $secondName';
});
//更改值得时候
 ref.read(fristNameProvider.notifier).state =
                     'Jane'
 ref.read(secondName.notifier).state =
                     'BB'
        2、使用ConsumerWidget更新UI
ref.read(surnameProvider) 读取某个值
ref.read(nameProvider.notifier).state 更新某个值的状态
            
            
              scala
              
              
            
          
          class MyHomePage extends ConsumerWidget {
  const MyHomePage({super.key});
  @override
  Widget build(BuildContext context, WidgetRef ref) => 
     Scaffold(
        appBar: AppBar(
          title: const Text('Riverpod Example'),
        ),
        body: Text(
               ref.watch(fullNameProvider),
               style: Theme.of(context).textTheme.headlineMedium,
            ),
      );
}
        这里Consumer组件是与状态交互所必需的,Consumer有一个非标准build方法,这意味着如果您需要更改状态管理解决方案,您还必须更改组件而不仅仅是状态。
RxDart
RxDart将ReactiveX的强大功能引入Flutter,需要明确的逻辑来组合不同的数据流并对其做出反应。
存储计算值:它不会以有状态的方式直接存储计算值,但它确实提供了有用的运算符(例如distinctUnique)来帮助您最大限度地减少重新计算。
RxDart 库还有一个流行的类型被称为BehaviorSubject。响应式编程试图解决的核心问题是当依赖图中的任何值(依赖项)发生变化时自动触发计算。如果有多个可观察值,并且您需要将它们合并到计算中,Rx 库自动为我们执行此操作并且自动最小化重新计算以提高性能。
该库向 Dart 的现有流添加了功能。它不会重新发明轮子,并使用其他平台上的开发人员熟悉的模式。
1、监听
            
            
              ini
              
              
            
          
           final fristName = BehaviorSubject.seeded('Tom');
 final secondName = BehaviorSubject.seeded('Joy');
 
 /// 更新值
  fristName.add('Jane'),
  secondName.add('Jane'),
        2、使用StreamBuilder更新UI
            
            
              less
              
              
            
          
           StreamBuilder<String>(
        stream: Rx.combineLatest2(
            fristName,
            secondName,
            (fristName, secondName) => '$fristName $secondName',
             ),
             builder: (context, snapshot) => Text(
               snapshot.data ?? '',
               style: Theme.of(context).textTheme.headlineMedium,
              ),
    ),
        Signals
Signals以其computed功能介绍了一种创新、优雅的解决方案。它会自动创建反应式计算,当任何依赖值发生变化时,反应式计算就会更新。
1、监听
            
            
              dart
              
              
            
          
            final name = signal('Jane');
  final surname = signal('Doe');
  late final ReadonlySignal<String> fullName =
      computed(() => '${name.value} ${surname.value}');
  late final void Function() _dispose;
 @override
  void initState() {
    super.initState();
    _dispose = effect(() => fullName.value);
  }
        2、使用watch更新UI
            
            
              less
              
              
            
          
          Text(
    fullName.watch(context),
    style: Theme.of(context).textTheme.headlineMedium,
 ),