Skip to content

Commit

Permalink
Merge pull request #24 from leancodepl/feat/generic-presentation-event
Browse files Browse the repository at this point in the history
  • Loading branch information
shilangyu authored Aug 30, 2023
2 parents 5e1731e + 4bf96f2 commit badcc67
Show file tree
Hide file tree
Showing 18 changed files with 71 additions and 80 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/bloc_presentation-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ['3.10.x']
version: ['3.10.x', '3.13.x']

defaults:
run:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/bloc_presentation_test-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
strategy:
fail-fast: false
matrix:
version: ['3.10.x']
version: ['3.10.x', '3.13.x']

defaults:
run:
Expand Down
5 changes: 5 additions & 0 deletions packages/bloc_presentation/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# 0.4.0

- **BREAKING**: `BlocPresentationMixin` is now generic over the type of the presentation event (see #17)
- **BREAKING**: removed `BlocPresentationEvent` marker type

# 0.3.0

- Bump minimum Flutter version to `3.10.0` (#22)
Expand Down
17 changes: 10 additions & 7 deletions packages/bloc_presentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ flutter pub add bloc_presentation
First, create an event which will be emitted:

```dart
class FailedToUpvote implements BlocPresentationEvent {
sealed class CommentCubitEvent {}
class FailedToUpvote implements CommentCubitEvent {
const FailedToUpvote(this.reason);
final String reason;
Expand All @@ -28,7 +30,7 @@ Next, extend your Bloc/Cubit with the presentation mixin which will give you
access to the `emitPresentation` method:

```dart
class CommentCubit extends Cubit<CommentState> with BlocPresentationMixin {
class CommentCubit extends Cubit<CommentState> with BlocPresentationMixin<CommentState, CommentCubitEvent> {
// body
}
```
Expand All @@ -55,12 +57,13 @@ new states. Then, in the UI code one can react to such events using
`BlocPresentationListener` or `useBlocPresentationListener`:

```dart
BlocPresentationListener<CommentCubit>(
BlocPresentationListener<CommentCubit, CommentCubitEvent>(
listener: (context, event) {
if (event is FailedToUpvote) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.reason)));
switch (event) {
case FailedToUpvote():
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.reason)));
}
},
child: MyWidget(),
Expand Down
6 changes: 3 additions & 3 deletions packages/bloc_presentation/example/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
buildscript {
ext.kotlin_version = '1.3.50'
ext.kotlin_version = '1.8.21'
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:4.1.0'
classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand All @@ -26,6 +26,6 @@ subprojects {
project.evaluationDependsOn(':app')
}

task clean(type: Delete) {
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
9 changes: 6 additions & 3 deletions packages/bloc_presentation/example/lib/comment_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import 'dart:math';
import 'package:bloc_presentation/bloc_presentation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CommentCubit extends Cubit<CommentState> with BlocPresentationMixin {
class CommentCubit extends Cubit<CommentState>
with BlocPresentationMixin<CommentState, CommentEvent> {
CommentCubit() : super(const CommentInitialState());

void fetch() {
Expand Down Expand Up @@ -32,13 +33,15 @@ class CommentCubit extends Cubit<CommentState> with BlocPresentationMixin {
}
}

class FailedToUpvote implements BlocPresentationEvent {
sealed class CommentEvent {}

class FailedToUpvote implements CommentEvent {
const FailedToUpvote(this.reason);

final String reason;
}

class SuccessfulUpvote implements BlocPresentationEvent {
class SuccessfulUpvote implements CommentEvent {
const SuccessfulUpvote(this.message);

final String message;
Expand Down
19 changes: 10 additions & 9 deletions packages/bloc_presentation/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ class MyHomePage extends StatelessWidget {

@override
Widget build(BuildContext context) {
return BlocPresentationListener<CommentCubit>(
return BlocPresentationListener<CommentCubit, CommentEvent>(
listener: (context, event) {
// we know we will receive this event once
if (event is FailedToUpvote) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.reason)));
} else if (event is SuccessfulUpvote) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.message)));
switch (event) {
case FailedToUpvote():
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.reason)));
case SuccessfulUpvote():
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(event.message)));
}
},
child: Scaffold(
Expand Down
2 changes: 1 addition & 1 deletion packages/bloc_presentation/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ dev_dependencies:
bloc_test: ^9.1.2
flutter_test:
sdk: flutter
leancode_lint: '^5.0.0'
leancode_lint: ">=5.0.0 <7.0.0"

flutter:
uses-material-design: true
1 change: 0 additions & 1 deletion packages/bloc_presentation/lib/bloc_presentation.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export 'src/bloc_presentation_event.dart';
export 'src/bloc_presentation_listener.dart';
export 'src/bloc_presentation_mixin.dart';
export 'src/use_bloc_presentation_listener.dart';
17 changes: 0 additions & 17 deletions packages/bloc_presentation/lib/src/bloc_presentation_event.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:bloc_presentation/src/bloc_presentation_event.dart';
import 'package:bloc_presentation/src/use_bloc_presentation_listener.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
Expand All @@ -10,15 +9,15 @@ import 'bloc_presentation_mixin.dart';
/// Signature for the `listener` function which takes the `BuildContext` along
/// with the `event` and is responsible for executing in response to
/// new events.
typedef BlocPresentationWidgetListener = void Function(
typedef BlocPresentationWidgetListener<P> = void Function(
BuildContext context,
BlocPresentationEvent event,
P event,
);

/// {@template bloc_presentation_listener}
/// Widget that listens to new presentation events in a specified [bloc].
/// {@endtemplate}
class BlocPresentationListener<B extends BlocPresentationMixin<dynamic>>
class BlocPresentationListener<B extends BlocPresentationMixin<dynamic, P>, P>
extends SingleChildStatelessWidget {
/// {@macro bloc_presentation_listener}
const BlocPresentationListener({
Expand All @@ -34,7 +33,7 @@ class BlocPresentationListener<B extends BlocPresentationMixin<dynamic>>
final B? bloc;

/// The [BlocPresentationWidgetListener] which will be called on every new presentation event.
final BlocPresentationWidgetListener listener;
final BlocPresentationWidgetListener<P> listener;

@override
Widget buildWithChild(BuildContext context, Widget? child) {
Expand Down
11 changes: 4 additions & 7 deletions packages/bloc_presentation/lib/src/bloc_presentation_mixin.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import 'dart:async';

import 'package:bloc_presentation/src/bloc_presentation_event.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

/// [BlocPresentationMixin] adds a presentation stream to a [BlocBase]
/// which is automatically disposed.
mixin BlocPresentationMixin<S> on BlocBase<S> {
final _presentationStream =
StreamController<BlocPresentationEvent>.broadcast();
mixin BlocPresentationMixin<S, P> on BlocBase<S> {
final _presentationStream = StreamController<P>.broadcast();

/// Stream emitting non-unique presentation events.
Stream<BlocPresentationEvent> get presentation => _presentationStream.stream;
Stream<P> get presentation => _presentationStream.stream;

/// Emits a new presentation event.
@protected
void emitPresentation(BlocPresentationEvent event) =>
_presentationStream.add(event);
void emitPresentation(P event) => _presentationStream.add(event);

@override
@mustCallSuper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ import 'package:provider/provider.dart';
///
/// If [bloc] is omitted, [useBlocPresentationListener] will automatically perform
/// a lookup using [Provider] and the current `BuildContext`.
void useBlocPresentationListener<B extends BlocPresentationMixin<dynamic>>({
required BlocPresentationWidgetListener listener,
void useBlocPresentationListener<B extends BlocPresentationMixin<dynamic, P>,
P>({
required BlocPresentationWidgetListener<P> listener,
B? bloc,
}) {
final context = useContext();
final effectiveStream = bloc?.presentation ??
context.select<B, Stream<BlocPresentationEvent>>(
(bloc) => bloc.presentation,
);
context.select<B, Stream<P>>((bloc) => bloc.presentation);

useEffect(
() {
Expand Down
2 changes: 1 addition & 1 deletion packages/bloc_presentation/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: bloc_presentation
description: >
Extends blocs with an additional stream which can serve as a way of indicating
single-time events (so-called "presentation events").
version: 0.3.0
version: 0.4.0
homepage: https://github.com/leancodepl/bloc_presentation/tree/master/packages/bloc_presentation

environment:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ import 'package:bloc_presentation/bloc_presentation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';

class _TestCubit extends Cubit<int> with BlocPresentationMixin {
class _TestCubit extends Cubit<int> with BlocPresentationMixin<int, _Event> {
_TestCubit() : super(0);

void emitValueEvent() => emitPresentation(_ValueEvent());
void emitReferenceEvent() => emitPresentation(_ReferenceEvent());
}

class _ValueEvent implements BlocPresentationEvent {
sealed class _Event {}

class _ValueEvent implements _Event {
@override
bool operator ==(Object other) => other is _ValueEvent;

@override
int get hashCode => 0;
}

class _ReferenceEvent implements BlocPresentationEvent {}
class _ReferenceEvent implements _Event {}

void main() {
group('BlocPresentationMixin', () {
Expand Down
6 changes: 0 additions & 6 deletions packages/bloc_presentation/test/fakes.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import 'package:bloc_presentation/bloc_presentation.dart';
import 'package:flutter/material.dart';
import 'package:mocktail/mocktail.dart';

class _FakeBuildContext extends Fake implements BuildContext {}

class _FakeBlocPresentationEvent extends Fake implements BlocPresentationEvent {
// Flutter 3.3 and 3.7 format this class differently, hence the comment...
}

void registerFakes() {
registerFallbackValue(_FakeBuildContext());
registerFallbackValue(_FakeBlocPresentationEvent());
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,33 @@ import 'package:provider/provider.dart';

import 'fakes.dart';

class _TestCubit extends Cubit<int> with BlocPresentationMixin {
class _TestCubit extends Cubit<int>
with BlocPresentationMixin<int, _PresentationEvent> {
_TestCubit() : super(0);

void emitEvent(BlocPresentationEvent event) => emitPresentation(event);
void emitEvent(_PresentationEvent event) => emitPresentation(event);
}

class _PresentationEvent implements BlocPresentationEvent {}
class _PresentationEvent {}

class _MockListener extends Mock {
void call(
BuildContext context,
BlocPresentationEvent event,
_PresentationEvent event,
);
}

void main() {
group('useBlocPresentationListener', () {
late _TestCubit cubit;
late BlocPresentationWidgetListener listener;
late BlocPresentationEvent event;
late BlocPresentationWidgetListener<_PresentationEvent> listener;
late _PresentationEvent event;
late HookElement element;

setUpAll(registerFakes);
setUpAll(() {
registerFakes();
registerFallbackValue(_PresentationEvent());
});

setUp(() {
cubit = _TestCubit();
Expand Down Expand Up @@ -70,7 +74,9 @@ void main() {
child: HookBuilder(
builder: (context) {
element = context as HookElement;
useBlocPresentationListener<_TestCubit>(listener: listener);
useBlocPresentationListener<_TestCubit, _PresentationEvent>(
listener: listener,
);

return Container();
},
Expand Down Expand Up @@ -172,7 +178,7 @@ void main() {
await tester.pumpWidget(
HookBuilder(
builder: (context) {
useBlocPresentationListener(
useBlocPresentationListener<_TestCubit, _PresentationEvent>(
listener: listener2,
bloc: cubit2,
);
Expand Down

0 comments on commit badcc67

Please sign in to comment.