Skip to content
This repository has been archived by the owner on May 28, 2024. It is now read-only.

Commit

Permalink
Merge pull request #26 from lunofono/button-image
Browse files Browse the repository at this point in the history
Add buttons with images
  • Loading branch information
llucax authored Feb 18, 2021
2 parents eee285b + adce590 commit d445ff6
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 19 deletions.
1 change: 1 addition & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ final bundle = Bundle(
),
),
backgroundColor: Colors.amber,
foregroundImage: Uri.parse('assets/heilshorn-cows.jpg'),
),
StyledButton(
PlayContentAction(
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ dependencies:
lunofono_bundle:
git:
url: https://github.com/lunofono/lunofono_bundle.git
ref: v0.2.0
ref: v0.3.0

dev_dependencies:
flutter_test:
Expand Down
6 changes: 5 additions & 1 deletion lib/src/button_player.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'package:flutter/material.dart' hide Action;

import 'package:lunofono_bundle/lunofono_bundle.dart'
show Action, Button, Color, StyledButton;
show Action, Button, Color, StyledButton, ImageButton;
import 'package:lunofono_player/src/button_player/image_button_player.dart';

import 'action_player.dart' show ActionPlayer;
import 'button_player/image_button_player.dart' show ImageButtonPlayer;
import 'button_player/styled_button_player.dart' show StyledButtonPlayer;
import 'dynamic_dispatch_registry.dart' show DynamicDispatchRegistry;

Expand All @@ -15,6 +17,8 @@ void _registerBuiltin(ButtonPlayerRegistry registry) {
// New wrappers should be registered here
registry.register(
StyledButton, (button) => StyledButtonPlayer(button as StyledButton));
registry.register(
ImageButton, (button) => ImageButtonPlayer(button as ImageButton));
}

/// A wrapper to manage how a [Button] is played by the player.
Expand Down
49 changes: 49 additions & 0 deletions lib/src/button_player/image_button_player.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';

import 'package:lunofono_bundle/lunofono_bundle.dart'
show Button, Color, ImageButton;

import '../button_player.dart' show ButtonPlayer;

/// A wrapper to play a [ImageButton].
class ImageButtonPlayer extends ButtonPlayer {
/// The underlaying model's [Button].
@override
final ImageButton button;

/// Constructs a [ButtonPlayer] using [button] as the underlaying [Button].
ImageButtonPlayer(this.button)
: assert(button != null),
super(button);

/// The background [Color] of the underlaying [button].
///
/// TODO: This is very hacky and should be removed.
/// https://github.com/lunofono/lunofono_player/issues/25
@override
Color get backgroundColor => Colors.white;

/// The location of the image of the underlaying [button].
Uri get imageUri => button.imageUri;

@override
Widget build(BuildContext context) =>
ImageButtonWidget(button: this, key: ObjectKey(button));
}

/// A widget to display a [ImageButton].
class ImageButtonWidget extends StatelessWidget {
/// The button to display.
final ImageButtonPlayer button;

/// Creates a new [ImageButtonWidget] to display [button].
const ImageButtonWidget({@required this.button, Key key})
: assert(button != null),
super(key: key);

@override
Widget build(BuildContext context) => GestureDetector(
onTap: () => button.action.act(context, button),
child: Image.asset(button.imageUri.toString()),
);
}
32 changes: 18 additions & 14 deletions lib/src/button_player/styled_button_player.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,13 @@ class StyledButtonPlayer extends ButtonPlayer {
: assert(button != null),
super(button);

/// The [Color] of the underlaying [button].
/// The background [Color] of the underlaying [button].
@override
Color get backgroundColor => button.backgroundColor;

/// Creates a [GridButtonItem].
///
/// It uses [color] as the [GridButtonItem.color] and [this] as the
/// [GridButtonItem.value] and as a [ValueKey] for [GridButtonItem.key].
/// The foreground image [Uri] of the underlaying [button].
Uri get foregroundImage => button.foregroundImage;

@override
Widget build(BuildContext context) =>
StyledButtonWidget(button: this, key: ObjectKey(button));
Expand All @@ -40,14 +39,19 @@ class StyledButtonWidget extends StatelessWidget {
super(key: key);

@override
Widget build(BuildContext context) => TextButton(
onPressed: () => button.action.act(context, button),
child: const Text(''),
style: TextButton.styleFrom(
backgroundColor: button.backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
Widget build(BuildContext context) {
final child = button.foregroundImage == null
? const Text('')
: Image.asset(button.foregroundImage.toString());
return TextButton(
onPressed: () => button.action.act(context, button),
child: child,
style: TextButton.styleFrom(
backgroundColor: button.backgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50),
),
);
),
);
}
}
4 changes: 2 additions & 2 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "v0.2.0"
resolved-ref: "521720f448ea2b41a42a9a63c18c30fa72ee5f82"
ref: "v0.3.0"
resolved-ref: c33f53545ab549cb7a8aaad58b04445e8b5178de
url: "https://github.com/lunofono/lunofono_bundle.git"
source: git
version: "1.0.0"
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies:
lunofono_bundle:
git:
url: https://github.com/lunofono/lunofono_bundle.git
ref: v0.2.0
ref: v0.3.0

pausable_timer: ^0.1.0
provider: ^4.3.2+2
Expand Down
108 changes: 108 additions & 0 deletions test/unit/button_player/image_button_player_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
@Tags(['unit', 'player'])

import 'package:flutter/material.dart' hide Action;

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';

import 'package:lunofono_bundle/lunofono_bundle.dart' show Action, ImageButton;

import 'package:lunofono_player/src/action_player.dart';
import 'package:lunofono_player/src/button_player.dart';

import 'package:lunofono_player/src/button_player/image_button_player.dart';

import '../../util/test_asset_bundle.dart' show TestAssetBundle;

class FakeAction extends Action {
final actCalls = <ButtonPlayer>[];
}

class FakeActionPlayer extends ActionPlayer {
@override
final FakeAction action;
@override
void act(BuildContext context, ButtonPlayer button) =>
action.actCalls.add(button);
FakeActionPlayer(this.action) : assert(action != null);
}

class FakeContext extends Fake implements BuildContext {}

void main() {
final imageUri = Uri.parse('assets/10x10-red.png');
final oldActionRegistry = ActionPlayer.registry;

setUp(() {
ActionPlayer.registry = ActionPlayerRegistry();
ActionPlayer.registry
.register(FakeAction, (a) => FakeActionPlayer(a as FakeAction));
});

tearDown(() => ActionPlayer.registry = oldActionRegistry);

group('ImageButtonPlayer', () {
FakeContext fakeContext;

setUp(() {
fakeContext = FakeContext();
});

test('constructor asserts on null', () {
expect(() => ImageButtonPlayer(null), throwsAssertionError);
});

test('build creates a ImageButtonWidget', () {
final button = ImageButton(FakeAction(), imageUri);
final buttonPlayer = ButtonPlayer.wrap(button);
expect(buttonPlayer.button, same(button));
expect((buttonPlayer as ImageButtonPlayer).imageUri, button.imageUri);
final widget = buttonPlayer.build(fakeContext);
expect(widget.key, ObjectKey(button));
});
});

group('ImageButtonWidget', () {
test('constructor asserts on null button', () {
expect(() => ImageButtonWidget(button: null), throwsAssertionError);
});

testWidgets('tapping calls action.act()', (tester) async {
final action = FakeAction();
final button = ImageButton(action, imageUri);
final buttonPlayer = ButtonPlayer.wrap(button);
Widget widget;
await tester.pumpWidget(
MaterialApp(
home: DefaultAssetBundle(
bundle: TestAssetBundle(),
child: Builder(builder: (context) {
widget = buttonPlayer.build(context);
return widget;
}),
),
),
);
expect(widget.key, ObjectKey(button));
expect(widget, isA<ImageButtonWidget>());
expect((widget as ImageButtonWidget).button, same(buttonPlayer));
expect(action.actCalls.length, 0);
final buttonFinder = find.byKey(ObjectKey(button));
expect(buttonFinder, findsOneWidget);
final imageFinder = find.byType(Image);
expect(imageFinder, findsOneWidget);

// tap the button should call button.act()
await tester.tap(buttonFinder);
await tester.pump();
expect(action.actCalls.length, 1);
expect(action.actCalls.last, buttonPlayer);

// tap the image should call button.act()
await tester.tap(imageFinder);
await tester.pump();
expect(action.actCalls.length, 2);
expect(action.actCalls.last, buttonPlayer);
});
});
}
36 changes: 36 additions & 0 deletions test/unit/button_player/styled_button_player_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import 'package:lunofono_player/src/button_player.dart';

import 'package:lunofono_player/src/button_player/styled_button_player.dart';

import '../../util/test_asset_bundle.dart' show TestAssetBundle;

class FakeAction extends Action {
final actCalls = <ButtonPlayer>[];
}
Expand Down Expand Up @@ -84,6 +86,7 @@ void main() {
expect(widget.key, ObjectKey(button));
expect(widget, isA<StyledButtonWidget>());
expect((widget as StyledButtonWidget).button, same(buttonPlayer));
expect(find.byType(Image), findsNothing);
expect(action.actCalls.length, 0);

// tap the button should call button.act()
Expand All @@ -94,5 +97,38 @@ void main() {
expect(action.actCalls.length, 1);
expect(action.actCalls.last, buttonPlayer);
});

testWidgets(
'shows foregroundImage and tapping the image also calls action.act()',
(tester) async {
final action = FakeAction();
final button = StyledButton(action,
foregroundImage: Uri.parse('assets/10x10-red.png'));
final buttonPlayer = ButtonPlayer.wrap(button);
Widget widget;
await tester.pumpWidget(
MaterialApp(
home: DefaultAssetBundle(
bundle: TestAssetBundle(),
child: Builder(builder: (context) {
widget = buttonPlayer.build(context);
return widget;
}),
),
),
);
expect(widget.key, ObjectKey(button));
expect(widget, isA<StyledButtonWidget>());
expect((widget as StyledButtonWidget).button, same(buttonPlayer));
expect(action.actCalls.length, 0);
final imageFinder = find.byType(Image);
expect(imageFinder, findsOneWidget);

// tap the button should call button.act()
await tester.tap(imageFinder);
await tester.pump();
expect(action.actCalls.length, 1);
expect(action.actCalls.last, buttonPlayer);
});
});
}

0 comments on commit d445ff6

Please sign in to comment.