Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(flutter_bloc)!: remove package:provider #1880

Merged
merged 14 commits into from Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion 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
153 changes: 90 additions & 63 deletions packages/flutter_bloc/lib/src/bloc_provider.dart
@@ -1,18 +1,32 @@
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,
);
/// A function that creates a [Cubit] of type [T].
typedef CreateBloc<T extends Cubit<Object>> = T Function(BuildContext context);

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

/// Extends the [BuildContext] class with the ability
/// to perform a lookup based on a [Cubit] type.
extension BlocProviderExtension on BuildContext {
/// 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);
/// ```
T bloc<T extends Cubit<Object>>() => BlocProvider.of<T>(this);
}

/// {@template bloc_provider}
/// Takes a `ValueBuilder` that is responsible for creating the `bloc` and
/// a [child] which will have access to the `bloc` via
Expand All @@ -30,21 +44,16 @@ mixin BlocProviderSingleChildWidget on SingleChildWidget {}
/// );
/// ```
/// {@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,
);
@required this.create,
this.child,
this.lazy = true,
}) : assert(create != null),
super(key: key);

/// Takes a `bloc` and a [child] which will have access to the `bloc` via
/// `BlocProvider.of(context)`.
Expand All @@ -67,34 +76,20 @@ 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 [Cubit] of type [T].
final CreateBloc<T> create;

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

/// Whether or not the `bloc` being provided should be lazily created.
/// Whether the [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.
Expand All @@ -103,13 +98,20 @@ class BlocProvider<T extends Cubit<Object>> extends SingleChildStatelessWidget
/// 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.
Expand All @@ -121,29 +123,54 @@ class BlocProvider<T extends Cubit<Object>> extends SingleChildStatelessWidget
''',
);
}
return provider.value();
}
}

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 InheritedProvider<T>(
create: _create,
dispose: _dispose,
child: child,
lazy: lazy,
return _InheritedBlocProvider(
child: child ?? widget.child,
deferredBloc: _completer.future,
value: () {
if (!_completer.isCompleted) {
_bloc = widget.create(context);
_completer.complete(_bloc);
}
return _bloc;
},
);
}
}

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