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

Provider/v4.0.0 #734

Merged
merged 4 commits into from
Dec 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 15 additions & 26 deletions packages/flutter_bloc/lib/src/bloc_listener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'package:nested/nested.dart';
felangel marked this conversation as resolved.
Show resolved Hide resolved

/// Signature for the [listener] function which takes the `BuildContext` along with the [bloc] [state]
/// and is responsible for executing in response to [state] changes.
Expand Down Expand Up @@ -70,8 +71,8 @@ typedef BlocListenerCondition<S> = bool Function(S previous, S current);
/// )
/// ```
/// {@endtemplate}
class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S>
with SingleChildCloneableWidget {
class BlocListener<B extends Bloc<dynamic, S>, S>
extends BlocListenerBase<B, S> {
/// The widget which will be rendered as a descendant of the [BlocListener].
final Widget child;

Expand All @@ -85,26 +86,11 @@ class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S>
}) : assert(listener != null),
super(
key: key,
child: child,
listener: listener,
bloc: bloc,
condition: condition,
);

/// Clones the current [BlocListener] with a new [child] widget.
/// All other values, including [key], [bloc] and [listener] are preserved.
@override
BlocListener<B, S> cloneWithChild(Widget child) {
return BlocListener<B, S>(
key: key,
bloc: bloc,
listener: listener,
condition: condition,
child: child,
);
}

@override
Widget build(BuildContext context) => child;
}

/// {@template bloclistenerbase}
Expand All @@ -115,7 +101,10 @@ class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S>
/// is defined by sub-classes.
/// {@endtemplate}
abstract class BlocListenerBase<B extends Bloc<dynamic, S>, S>
extends StatefulWidget {
extends SingleChildStatefulWidget {
/// The widget which will be rendered as a descendant of the [BlocListenerBase].
final Widget child;

/// The [bloc] whose [state] will be listened to.
/// Whenever the [bloc]'s [state] changes, [listener] will be invoked.
final B bloc;
Expand All @@ -139,17 +128,17 @@ abstract class BlocListenerBase<B extends Bloc<dynamic, S>, S>
Key key,
this.listener,
this.bloc,
this.child,
this.condition,
}) : super(key: key);
}) : super(key: key, child: child);

State<BlocListenerBase<B, S>> createState() => _BlocListenerBaseState<B, S>();

/// Returns a widget based on the `BuildContext`.
Widget build(BuildContext context);
@override
SingleChildState<BlocListenerBase<B, S>> createState() =>
_BlocListenerBaseState<B, S>();
}

class _BlocListenerBaseState<B extends Bloc<dynamic, S>, S>
extends State<BlocListenerBase<B, S>> {
extends SingleChildState<BlocListenerBase<B, S>> {
StreamSubscription<S> _subscription;
S _previousState;
B _bloc;
Expand Down Expand Up @@ -178,7 +167,7 @@ class _BlocListenerBaseState<B extends Bloc<dynamic, S>, S>
}

@override
Widget build(BuildContext context) => widget.build(context);
Widget buildWithChild(BuildContext context, Widget child) => child;

@override
void dispose() {
Expand Down
40 changes: 17 additions & 23 deletions packages/flutter_bloc/lib/src/bloc_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,23 @@ import 'package:bloc/bloc.dart';
/// ```
/// {@endtemplate}
class BlocProvider<T extends Bloc<dynamic, dynamic>>
extends ValueDelegateWidget<T> implements SingleChildCloneableWidget {
extends SingleChildStatelessWidget {
/// [child] and its descendants which will have access to the [bloc].
final Widget child;

final Dispose<T> _dispose;

final Create<T> _create;

/// {@macro blocprovider}
BlocProvider({
Key key,
@Deprecated('will be removed in 3.0.0, use create instead')
ValueBuilder<T> builder,
@required ValueBuilder<T> create,
@required Create<T> create,
Widget child,
}) : this._(
key: key,
delegate: BuilderStateDelegate<T>(
// ignore: deprecated_member_use_from_same_package
create ?? builder,
dispose: (_, bloc) => bloc?.close(),
),
create: create,
dispose: (_, bloc) => bloc?.close(),
child: child,
);

Expand All @@ -58,17 +57,20 @@ class BlocProvider<T extends Bloc<dynamic, dynamic>>
Widget child,
}) : this._(
key: key,
delegate: SingleValueDelegate<T>(value),
create: (_) => value,
child: child,
);

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

/// Method that allows widgets to access a [bloc] instance as long as their `BuildContext`
/// contains a [BlocProvider] instance.
Expand Down Expand Up @@ -102,18 +104,10 @@ class BlocProvider<T extends Bloc<dynamic, dynamic>>
}

@override
Widget build(BuildContext context) {
Widget buildWithChild(BuildContext context, Widget child) {
return InheritedProvider<T>(
value: delegate.value,
child: child,
);
}

@override
BlocProvider<T> cloneWithChild(Widget child) {
return BlocProvider<T>._(
key: key,
delegate: delegate,
create: _create,
dispose: _dispose,
child: child,
);
}
Expand Down
7 changes: 2 additions & 5 deletions packages/flutter_bloc/lib/src/repository_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,11 @@ class RepositoryProvider<T> extends Provider<T> {
/// {@macro repositoryprovider}
RepositoryProvider({
Key key,
@required ValueBuilder<T> create,
@Deprecated('will be removed in 3.0.0, use create instead')
ValueBuilder<T> builder,
@required Create<T> create,
Widget child,
}) : super(
key: key,
// ignore: deprecated_member_use_from_same_package
create: create ?? builder,
create: create,
dispose: (_, __) {},
child: child,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_bloc/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ dependencies:
flutter:
sdk: flutter
bloc: ^3.0.0-dev.1
provider: ^3.2.0
provider: 4.0.0-dev

dev_dependencies:
flutter_test:
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter_bloc/test/bloc_builder_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ void main() {

themeBloc.add(SetDarkTheme());

await tester.pumpAndSettle();
await tester.pump();

var _materialApp = find.byKey(Key('material_app')).evaluate().first.widget
as MaterialApp;
Expand All @@ -287,7 +287,7 @@ void main() {

themeBloc.add(SetLightTheme());

await tester.pumpAndSettle();
await tester.pump();

_materialApp = find.byKey(Key('material_app')).evaluate().first.widget
as MaterialApp;
Expand Down
92 changes: 81 additions & 11 deletions packages/flutter_bloc/test/bloc_provider_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,23 @@ class RoutePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: RaisedButton(
key: Key('route_button'),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute<Widget>(builder: (context) => Container()),
);
},
body: Column(
children: [
RaisedButton(
key: Key('route_button'),
onPressed: () {
Navigator.of(context).pushReplacement(
MaterialPageRoute<Widget>(builder: (context) => Container()),
);
},
),
RaisedButton(
key: Key('increment_buton'),
onPressed: () {
BlocProvider.of<CounterBloc>(context).add(CounterEvent.increment);
},
),
],
),
);
}
Expand All @@ -170,10 +180,10 @@ class CounterBloc extends Bloc<CounterEvent, int> {
@override
Stream<int> mapEventToState(CounterEvent event) async* {
switch (event) {
case CounterEvent.decrement:
case CounterEvent.increment:
yield state + 1;
break;
case CounterEvent.increment:
case CounterEvent.decrement:
yield state - 1;
break;
}
Expand Down Expand Up @@ -253,7 +263,41 @@ void main() {
},
);

testWidgets('calls close on bloc automatically',
testWidgets(
'can access bloc directly within builder',
(WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: BlocProvider(
create: (context) => CounterBloc(),
child: BlocBuilder<CounterBloc, int>(
builder: (context, state) => Column(
children: [
Text('state: $state'),
RaisedButton(
key: Key('increment_button'),
onPressed: () {
BlocProvider.of<CounterBloc>(context)
.add(CounterEvent.increment);
},
),
],
),
),
),
),
),
);
expect(find.text('state: 0'), findsOneWidget);
await tester.tap(find.byKey(Key('increment_button')));
await tester.pump();
expect(tester.takeException(), isNull);
expect(find.text('state: 1'), findsOneWidget);
},
);

testWidgets('does not call close on bloc if it was not loaded (lazily)',
(WidgetTester tester) async {
var closeCalled = false;
CounterBloc _create(BuildContext context) => CounterBloc(
Expand All @@ -274,6 +318,32 @@ void main() {
await tester.tap(_routeButtonFinder);
await tester.pumpAndSettle();

expect(closeCalled, false);
});

testWidgets('calls close on bloc automatically when invoked (lazily)',
(WidgetTester tester) async {
var closeCalled = false;
CounterBloc _create(BuildContext context) => CounterBloc(
onClose: () {
closeCalled = true;
},
);
final Widget _child = RoutePage();
await tester.pumpWidget(MyApp(
create: _create,
child: _child,
));
final incrementButtonFinder = find.byKey(Key('increment_buton'));
expect(incrementButtonFinder, findsOneWidget);
await tester.tap(incrementButtonFinder);
final routeButtonFinder = find.byKey((Key('route_button')));
expect(routeButtonFinder, findsOneWidget);
expect(closeCalled, false);

await tester.tap(routeButtonFinder);
await tester.pumpAndSettle();

expect(closeCalled, true);
});

Expand Down Expand Up @@ -339,7 +409,7 @@ void main() {
child: _child,
));
await tester.tap(find.byKey(Key('iconButtonKey')));
await tester.pumpAndSettle();
await tester.pump();
expect(numBuilds, 1);
});
});
Expand Down
Loading