1
import 'dart:async';
2

3
import 'package:flutter/widgets.dart';
4

5
import 'provider.dart';
6

7
/// A callback used to build a valid value from an error.
8
///
9
/// See also:
10
///
11
///   * [StreamProvider] and [FutureProvider], which both uses [ErrorBuilder] to
12
///     handle respectively [Stream.catchError] and [Future.catch].
13
typedef ErrorBuilder<T> = T Function(BuildContext context, Object error);
14

15 3
DeferredStartListening<Stream<T>, T> _streamStartListening<T>({
16
  T initialData,
17
  ErrorBuilder<T> catchError,
18
}) {
19 3
  return (e, setState, controller, __) {
20 3
    if (!e.hasValue) {
21 3
      setState(initialData);
22
    }
23
    if (controller == null) {
24 3
      return () {};
25
    }
26 3
    final sub = controller.listen(
27
      setState,
28 3
      onError: (dynamic error) {
29
        if (catchError != null) {
30 3
          setState(catchError(e, error));
31
        } else {
32 3
          FlutterError.reportError(
33 3
            FlutterErrorDetails(
34
              library: 'provider',
35 3
              exception: FlutterError('''
36 3
An exception was throw by ${controller.runtimeType} listened by
37
StreamProvider<$T>, but no `catchError` was provided.
38

39
Exception:
40
$error
41 3
'''),
42
            ),
43
          );
44
        }
45
      },
46
    );
47

48 3
    return sub.cancel;
49
  };
50
}
51

52
/// Listens to a [Stream] and exposes its content to `child` and descendants.
53
///
54
/// Its main use-case is to provide to a large number of a widget the content
55
/// of a [Stream], without caring about reacting to events.
56
/// A typical example would be to expose the battery level, or a Firebase query.
57
///
58
/// Trying to use [Stream] to replace [ChangeNotifier] is outside of the scope
59
/// of this class.
60
///
61
/// It is considered an error to pass a stream that can emit errors without
62
/// providing a `catchError` method.
63
///
64
/// `initialData` determines the value exposed until the [Stream] emits a value.
65
/// If omitted, defaults to `null`.
66
///
67
/// By default, [StreamProvider] considers that the [Stream] listened uses
68
/// immutable data. As such, it will not rebuild dependents if the previous and
69
/// the new value are `==`.
70
/// To change this behavior, pass a custom `updateShouldNotify`.
71
///
72
/// See also:
73
///
74
///   * [Stream], which is listened by [StreamProvider].
75
///   * [StreamController], to create a [Stream].
76
class StreamProvider<T> extends DeferredInheritedProvider<Stream<T>, T> {
77
  /// Creates a [Stream] using `create` and subscribes to it.
78
  ///
79
  /// The parameter `create` must not be `null`.
80 3
  StreamProvider({
81
    Key key,
82
    @required Create<Stream<T>> create,
83
    T initialData,
84
    ErrorBuilder<T> catchError,
85
    UpdateShouldNotify<T> updateShouldNotify,
86
    bool lazy,
87
    TransitionBuilder builder,
88
    Widget child,
89 3
  })  : assert(create != null),
90 3
        super(
91
          key: key,
92
          lazy: lazy,
93
          builder: builder,
94
          create: create,
95
          updateShouldNotify: updateShouldNotify,
96 3
          startListening: _streamStartListening(
97
            catchError: catchError,
98
            initialData: initialData,
99
          ),
100
          child: child,
101
        );
102

103
  /// Listens to `value` and expose it to all of [StreamProvider] descendants.
104 3
  StreamProvider.value({
105
    Key key,
106
    @required Stream<T> value,
107
    T initialData,
108
    ErrorBuilder<T> catchError,
109
    UpdateShouldNotify<T> updateShouldNotify,
110
    bool lazy,
111
    TransitionBuilder builder,
112
    Widget child,
113 3
  }) : super.value(
114
          key: key,
115
          lazy: lazy,
116
          builder: builder,
117
          value: value,
118
          updateShouldNotify: updateShouldNotify,
119 3
          startListening: _streamStartListening(
120
            catchError: catchError,
121
            initialData: initialData,
122
          ),
123
          child: child,
124
        );
125
}
126

127 3
DeferredStartListening<Future<T>, T> _futureStartListening<T>({
128
  T initialData,
129
  ErrorBuilder<T> catchError,
130
}) {
131 3
  return (e, setState, controller, __) {
132 3
    if (!e.hasValue) {
133 3
      setState(initialData);
134
    }
135

136
    var canceled = false;
137 3
    controller?.then(
138 3
      (value) {
139
        if (canceled) return;
140 3
        setState(value);
141
      },
142 3
      onError: (dynamic error) {
143
        if (canceled) return;
144
        if (catchError != null) {
145 3
          setState(catchError(e, error));
146
        } else {
147 3
          FlutterError.reportError(
148 3
            FlutterErrorDetails(
149
              library: 'provider',
150 3
              exception: FlutterError('''
151 3
An exception was throw by ${controller.runtimeType} listened by
152
FutureProvider<$T>, but no `catchError` was provided.
153

154
Exception:
155
$error
156 3
'''),
157
            ),
158
          );
159
        }
160
      },
161
    );
162

163 3
    return () => canceled = true;
164
  };
165
}
166

167
/// Listens to a [Future] and exposes its result to `child` and its descendants.
168
///
169
/// It is considered an error to pass a future that can emit errors without
170
/// providing a `catchError` method.
171
///
172
/// {@macro provider.updateshouldnotify}
173
///
174
/// See also:
175
///
176
///   * [Future], which is listened by [FutureProvider].
177
class FutureProvider<T> extends DeferredInheritedProvider<Future<T>, T> {
178
  /// Creates a [Future] from `create` and subscribes to it.
179
  ///
180
  /// `create` must not be `null`.
181 3
  FutureProvider({
182
    Key key,
183
    @required Create<Future<T>> create,
184
    T initialData,
185
    ErrorBuilder<T> catchError,
186
    UpdateShouldNotify<T> updateShouldNotify,
187
    bool lazy,
188
    TransitionBuilder builder,
189
    Widget child,
190 3
  })  : assert(create != null),
191 3
        super(
192
          key: key,
193
          lazy: lazy,
194
          builder: builder,
195
          create: create,
196
          updateShouldNotify: updateShouldNotify,
197 3
          startListening: _futureStartListening(
198
            catchError: catchError,
199
            initialData: initialData,
200
          ),
201
          child: child,
202
        );
203

204
  /// Listens to `value` and expose it to all of [FutureProvider] descendants.
205 3
  FutureProvider.value({
206
    Key key,
207
    @required Future<T> value,
208
    T initialData,
209
    ErrorBuilder<T> catchError,
210
    UpdateShouldNotify<T> updateShouldNotify,
211
    TransitionBuilder builder,
212
    Widget child,
213 3
  }) : super.value(
214
          key: key,
215
          builder: builder,
216
          lazy: false,
217
          value: value,
218
          updateShouldNotify: updateShouldNotify,
219 3
          startListening: _futureStartListening(
220
            catchError: catchError,
221
            initialData: initialData,
222
          ),
223
          child: child,
224
        );
225
}

Read our documentation on viewing source code .

Loading