Skip to content

Commit

Permalink
refactor(flutter_bloc)!: remove package:provider (#1880)
Browse files Browse the repository at this point in the history
  • Loading branch information
felangel committed Nov 4, 2020
1 parent 10805eb commit 9f99cd2
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 155 deletions.
9 changes: 2 additions & 7 deletions packages/flutter_bloc/lib/src/bloc_listener.dart
Expand Up @@ -2,7 +2,7 @@ import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/single_child_widget.dart';
import 'package:nested/nested.dart';

import 'bloc_provider.dart';

Expand Down Expand Up @@ -87,7 +87,7 @@ class BlocListener<C extends Cubit<S>, S> extends BlocListenerBase<C, S>
@required BlocWidgetListener<S> listener,
C cubit,
BlocListenerCondition<S> listenWhen,
this.child,
Widget child,
}) : assert(listener != null),
super(
key: key,
Expand All @@ -96,11 +96,6 @@ class BlocListener<C extends Cubit<S>, S> extends BlocListenerBase<C, S>
cubit: cubit,
listenWhen: listenWhen,
);

/// The widget which will be rendered as a descendant of the [BlocListener].
@override
// ignore: overridden_fields
final Widget child;
}

/// {@template bloc_listener_base}
Expand Down
197 changes: 117 additions & 80 deletions packages/flutter_bloc/lib/src/bloc_provider.dart
@@ -1,61 +1,66 @@
import 'dart:async';

import 'package:flutter/widgets.dart';

import 'package:bloc/bloc.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'package:inherited_stream/inherited_stream.dart';
import 'package:nested/nested.dart';

/// A function that creates a `Bloc` of type [T].
typedef CreateBloc<T extends Cubit<dynamic>> = T Function(
BuildContext context,
);
/// Function that creates a [Bloc] or [Cubit] of type [T].
typedef _Create<T extends Cubit<Object>> = T Function(BuildContext context);

/// Mixin which allows `MultiBlocProvider` to infer the types
/// of multiple [BlocProvider]s.
mixin BlocProviderSingleChildWidget on SingleChildWidget {}

/// {@template bloc_provider}
/// Takes a `ValueBuilder` that is responsible for creating the `bloc` and
/// a [child] which will have access to the `bloc` via
/// `BlocProvider.of(context)`.
/// Takes a [create] function that is responsible for
/// creating the [Bloc] or [Cubit] and a [child] which will have access
/// to the instance via `BlocProvider.of(context)`.
/// It is used as a dependency injection (DI) widget so that a single instance
/// of a `bloc` can be provided to multiple widgets within a subtree.
/// of a [Bloc] or [Cubit] can be provided to multiple widgets within a subtree.
///
/// Automatically handles closing the `bloc` when used with `create` and lazily
/// creates the provided `bloc` unless [lazy] is set to `false`.
/// ```dart
/// BlocProvider(
/// create: (BuildContext context) => BlocA(),
/// child: ChildA(),
/// );
/// ```
///
/// It automatically handles closing the instance when used with [create].
/// By default, [create] is called only when the instance is accessed.
/// To override this behavior, set [lazy] to `false`.
///
/// ```dart
/// BlocProvider(
/// lazy: false,
/// create: (BuildContext context) => BlocA(),
/// child: ChildA(),
/// );
/// ```
///
/// {@endtemplate}
class BlocProvider<T extends Cubit<Object>> extends SingleChildStatelessWidget
class BlocProvider<T extends Cubit<Object>> extends SingleChildStatefulWidget
with BlocProviderSingleChildWidget {
/// {@macro bloc_provider}
BlocProvider({
const BlocProvider({
Key key,
@required CreateBloc<T> create,
Widget child,
bool lazy,
}) : this._(
key: key,
create: create,
dispose: (_, bloc) => bloc?.close(),
child: child,
lazy: lazy,
);

/// Takes a `bloc` and a [child] which will have access to the `bloc` via
@required this.create,
this.child,
this.lazy = true,
}) : assert(create != null),
super(key: key);

/// Takes a [value] and a [child] which will have access to the [value] via
/// `BlocProvider.of(context)`.
/// When `BlocProvider.value` is used, the `bloc` will not be automatically
/// closed.
/// As a result, `BlocProvider.value` should mainly be used for providing
/// existing `bloc`s to new routes.
/// When `BlocProvider.value` is used, the [Bloc] or [Cubit]
/// will not be automatically closed.
/// As a result, `BlocProvider.value` should only be used for providing
/// existing instances to new subtrees.
///
/// A new `bloc` should not be created in `BlocProvider.value`.
/// `bloc`s should always be created using the default constructor within
/// `create`.
/// A new [Bloc] or [Cubit] should not be created in `BlocProvider.value`.
/// New instances should always be created using the
/// default constructor within the [create] function.
///
/// ```dart
/// BlocProvider.value(
Expand All @@ -67,52 +72,45 @@ class BlocProvider<T extends Cubit<Object>> extends SingleChildStatelessWidget
Key key,
@required T value,
Widget child,
}) : this._(
key: key,
create: (_) => value,
child: child,
);

/// Internal constructor responsible for creating the [BlocProvider].
/// Used by the [BlocProvider] default and value constructors.
BlocProvider._({
Key key,
@required Create<T> create,
Dispose<T> dispose,
this.child,
this.lazy,
}) : _create = create,
_dispose = dispose,
super(key: key, child: child);
}) : this(key: key, create: (_) => value, child: child);

/// [child] and its descendants which will have access to the `bloc`.
/// Creates a [Bloc] or [Cubit] of type [T].
final _Create<T> create;

/// Widget which will have access to the [Bloc] or [Cubit].
final Widget child;

/// Whether or not the `bloc` being provided should be lazily created.
/// Whether the [Bloc] or [Cubit] should be created lazily.
/// Defaults to `true`.
final bool lazy;

final Dispose<T> _dispose;

final Create<T> _create;
@override
_BlocProviderState<T> createState() => _BlocProviderState<T>();

/// Method that allows widgets to access a `cubit` instance as long as their
/// `BuildContext` contains a [BlocProvider] instance.
/// Method that allows widgets to access a [Bloc] or [Cubit] instance
/// as long as their `BuildContext` contains a [BlocProvider] instance.
///
/// If we want to access an instance of `BlocA` which was provided higher up
/// in the widget tree we can do so via:
///
/// ```dart
/// BlocProvider.of<BlocA>(context)
/// BlocProvider.of<BlocA>(context);
/// ```
static T of<T extends Cubit<Object>>(BuildContext context) {
try {
return Provider.of<T>(context, listen: false);
} on ProviderNotFoundException catch (e) {
if (e.valueType != T) rethrow;
static T of<T extends Cubit<Object>>(
BuildContext context, {
bool listen = false,
}) {
final provider = listen
? context
.dependOnInheritedWidgetOfExactType<_InheritedBlocProvider<T>>()
: context
.getElementForInheritedWidgetOfExactType<
_InheritedBlocProvider<T>>()
?.widget as _InheritedBlocProvider<T>;
if (provider == null) {
throw FlutterError(
'''
BlocProvider.of() called with a context that does not contain a Cubit of type $T.
BlocProvider.of() called with a context that does not contain a Bloc/Cubit of type $T.
No ancestor could be found starting from the context that was passed to BlocProvider.of<$T>().
This can happen if the context you used comes from a widget above the BlocProvider.
Expand All @@ -121,29 +119,68 @@ class BlocProvider<T extends Cubit<Object>> extends SingleChildStatelessWidget
''',
);
}
}

@override
Widget buildWithChild(BuildContext context, Widget child) {
return InheritedProvider<T>(
create: _create,
dispose: _dispose,
child: child,
lazy: lazy,
);
return provider.value();
}
}

/// Extends the `BuildContext` class with the ability
/// to perform a lookup based on a `Bloc` type.
/// Extends the [BuildContext] class with the ability
/// to perform a lookup based on a [Bloc] or [Cubit] type.
extension BlocProviderExtension on BuildContext {
/// Performs a lookup using the `BuildContext` to obtain
/// the nearest ancestor `Cubit` of type [C].
/// Performs a lookup using the [BuildContext] to obtain
/// the nearest ancestor [Cubit] of type [T].
///
/// Calling this method is equivalent to calling:
///
/// ```dart
/// BlocProvider.of<C>(context)
/// BlocProvider.of<T>(context);
/// ```
C bloc<C extends Cubit<Object>>() => BlocProvider.of<C>(this);
T bloc<T extends Cubit<Object>>() => BlocProvider.of<T>(this);
}

class _BlocProviderState<T extends Cubit<Object>>
extends SingleChildState<BlocProvider<T>> {
T _bloc;
final _completer = Completer<T>();

@override
void initState() {
super.initState();
if (!widget.lazy) {
_bloc = widget.create(context);
_completer.complete(_bloc);
}
}

@override
void dispose() {
_bloc?.close();
super.dispose();
}

@override
Widget buildWithChild(BuildContext context, Widget child) {
return _InheritedBlocProvider(
child: child ?? widget.child,
deferredBloc: _completer.future,
value: () {
if (!_completer.isCompleted) {
_bloc = widget.create(context);
_completer.complete(_bloc);
}
return _bloc;
},
);
}
}

class _InheritedBlocProvider<T extends Cubit<Object>>
extends DeferredInheritedStream<T> {
_InheritedBlocProvider({
Key key,
@required Future<T> deferredBloc,
@required this.value,
Widget child,
}) : super(key: key, deferredStream: deferredBloc, child: child);

final ValueGetter<T> value;
}
6 changes: 3 additions & 3 deletions packages/flutter_bloc/lib/src/multi_bloc_listener.dart
@@ -1,5 +1,5 @@
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:nested/nested.dart';

import 'bloc_listener.dart';

Expand Down Expand Up @@ -48,13 +48,13 @@ import 'bloc_listener.dart';
/// As a result, the only advantage of using [MultiBlocListener] is improved
/// readability due to the reduction in nesting and boilerplate.
/// {@endtemplate}
class MultiBlocListener extends MultiProvider {
class MultiBlocListener extends Nested {
/// {@macro multi_bloc_listener}
MultiBlocListener({
Key key,
@required List<BlocListenerSingleChildWidget> listeners,
@required Widget child,
}) : assert(listeners != null),
assert(child != null),
super(key: key, providers: listeners, child: child);
super(key: key, children: listeners, child: child);
}
6 changes: 3 additions & 3 deletions packages/flutter_bloc/lib/src/multi_bloc_provider.dart
@@ -1,5 +1,5 @@
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:nested/nested.dart';

import 'bloc_provider.dart';

Expand Down Expand Up @@ -48,13 +48,13 @@ import 'bloc_provider.dart';
/// As a result, the only advantage of using [MultiBlocProvider] is improved
/// readability due to the reduction in nesting and boilerplate.
/// {@endtemplate}
class MultiBlocProvider extends MultiProvider {
class MultiBlocProvider extends Nested {
/// {@macro multi_bloc_provider}
MultiBlocProvider({
Key key,
@required List<BlocProviderSingleChildWidget> providers,
@required Widget child,
}) : assert(providers != null),
assert(child != null),
super(key: key, providers: providers, child: child);
super(key: key, children: providers, child: child);
}
6 changes: 3 additions & 3 deletions packages/flutter_bloc/lib/src/multi_repository_provider.dart
@@ -1,5 +1,5 @@
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:nested/nested.dart';

import 'repository_provider.dart';

Expand Down Expand Up @@ -42,13 +42,13 @@ import 'repository_provider.dart';
/// As a result, the only advantage of using [MultiRepositoryProvider] is
/// improved readability due to the reduction in nesting and boilerplate.
/// {@endtemplate}
class MultiRepositoryProvider extends MultiProvider {
class MultiRepositoryProvider extends Nested {
/// {@macro multi_repository_provider}
MultiRepositoryProvider({
Key key,
@required List<RepositoryProviderSingleChildWidget> providers,
@required Widget child,
}) : assert(providers != null),
assert(child != null),
super(key: key, providers: providers, child: child);
super(key: key, children: providers, child: child);
}

0 comments on commit 9f99cd2

Please sign in to comment.