feat: add GalleryHeader/GalleryFooter theme classes (#546)

* chore: Add ImageHeaderTheme and ImageHeaderThemeData to stream_chat_theme.dart

This commit adds the following classes to `stream_chat_theme.dart`:
1. `ImageHeaderTheme`, an `InheritedTheme` widget that can wrap other widgets and provide that sub-tree with an `ImageHeaderThemeData`
2. `ImageHeaderThemeData`, a class that allows for the customization of `ImageHeader` widgets

* Make ImageHeaderThemeData constructor const

* test: initial ImageHeaderThemeData tests

Tests the copyWith, ==, and hashcode functions

* test: add tests for default ImageHeaderThemeData values

Tests the default values of ImageHeaderThemeData that gets built in a StreamChatThemeData against control values.

* Update test name and add comments describing control

* fix: use ImageHeaderTheme.of(context) instead of StreamChatTheme.of(context)

This ensures that ImageHeader will always look for the closest ImageHeaderThemeData in the widget tree.

* test: add dark theme default values test

* run dartfmt on test file

* test: lerping between ImageHeaderThemeData

Added tests that lerp from one ImageHeaderThemeData to another (light to dark and dark to light)

* test: merging ImageHeaderThemeData

Added tests that merge light and dark ImageHeaderThemeData together (light + dark = dark, dark + light = light)

* test: add a half-lerp test

Added a test that lerps halway between light and dark ImageHeaderThemeData

* chore: Add ImageFooterTheme and ImageFooterThemeData to stream_chat_theme.dart

This commit adds the following classes to `stream_chat_theme.dart`:
1. `ImageFooterTheme`, an `InheritedTheme` widget that can wrap other widgets and provide that sub-tree with an `ImageFooterThemeData`
2. `ImageFooterThemeData`, a class that allows for the customization of `ImageFooter` widget

* chore: apply ImageFooterThemeData to image_footer.dart

* fix: make ImageFooterThemeData constructor const

* test: add tests for ImageFooterThemeData

* add a missing trailing comma

* chore: rename ImageHeader & ImageFooter to GalleryHeader and GalleryFooter

The respective theme classes and tests have also been renamed

* Run dartfmt on test

* test: fix failing tests due to renaming
This commit is contained in:
Reuben Turner
2021-07-15 15:44:12 +02:00
committed by GitHub
parent 6399e8f2db
commit 485e077596
8 changed files with 874 additions and 63 deletions
@@ -0,0 +1,185 @@
import 'package:flutter/material.dart' hide TextTheme;
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:mocktail/mocktail.dart';
class MockStreamChatClient extends Mock implements StreamChatClient {}
void main() {
test('GalleryFooterThemeData copyWith, ==, hashCode basics', () {
expect(const GalleryFooterThemeData(),
const GalleryFooterThemeData().copyWith());
expect(const GalleryFooterThemeData().hashCode,
const GalleryFooterThemeData().copyWith().hashCode);
});
test(
'Light GalleryFooterThemeData lerps completely to dark GalleryFooterThemeData',
() {
expect(
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControl,
_galleryFooterThemeDataControlDark, 1),
_galleryFooterThemeDataControlDark);
});
test(
'Light GalleryFooterThemeData lerps halfway to dark GalleryFooterThemeData',
() {
expect(
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControl,
_galleryFooterThemeDataControlDark, 0.5),
_galleryFooterThemeDataControlMidLerp);
});
test(
'Dark GalleryFooterThemeData lerps completely to light GalleryFooterThemeData',
() {
expect(
const GalleryFooterThemeData().lerp(_galleryFooterThemeDataControlDark,
_galleryFooterThemeDataControl, 1),
_galleryFooterThemeDataControl);
});
test('Merging dark and light themes results in a dark theme', () {
expect(
_galleryFooterThemeDataControl
.merge(_galleryFooterThemeDataControlDark),
_galleryFooterThemeDataControlDark);
});
test('Merging dark and light themes results in a dark theme', () {
expect(
_galleryFooterThemeDataControlDark
.merge(_galleryFooterThemeDataControl),
_galleryFooterThemeDataControl);
});
testWidgets(
'Passing no GalleryFooterThemeData returns default light theme values',
(WidgetTester tester) async {
late BuildContext _context;
await tester.pumpWidget(
MaterialApp(
builder: (context, child) => StreamChat(
client: MockStreamChatClient(),
child: child,
),
home: Builder(
builder: (context) {
_context = context;
return Scaffold(
appBar: GalleryFooter(
message: Message(),
),
);
},
),
),
);
final imageFooterTheme = GalleryFooterTheme.of(_context);
expect(imageFooterTheme.backgroundColor,
_galleryFooterThemeDataControl.backgroundColor);
expect(imageFooterTheme.shareIconColor,
_galleryFooterThemeDataControl.shareIconColor);
expect(imageFooterTheme.titleTextStyle,
_galleryFooterThemeDataControl.titleTextStyle);
expect(imageFooterTheme.gridIconButtonColor,
_galleryFooterThemeDataControl.gridIconButtonColor);
expect(imageFooterTheme.bottomSheetBarrierColor,
_galleryFooterThemeDataControl.bottomSheetBarrierColor);
expect(imageFooterTheme.bottomSheetBackgroundColor,
_galleryFooterThemeDataControl.bottomSheetBackgroundColor);
expect(imageFooterTheme.bottomSheetCloseIconColor,
_galleryFooterThemeDataControl.bottomSheetCloseIconColor);
expect(imageFooterTheme.bottomSheetPhotosTextStyle,
_galleryFooterThemeDataControl.bottomSheetPhotosTextStyle);
});
testWidgets(
'Passing no GalleryFooterThemeData returns default dark theme values',
(WidgetTester tester) async {
late BuildContext _context;
await tester.pumpWidget(
MaterialApp(
builder: (context, child) => StreamChat(
client: MockStreamChatClient(),
streamChatThemeData: StreamChatThemeData.dark(),
child: child,
),
home: Builder(
builder: (context) {
_context = context;
return Scaffold(
appBar: GalleryFooter(
message: Message(),
),
);
},
),
),
);
final imageFooterTheme = GalleryFooterTheme.of(_context);
expect(imageFooterTheme.backgroundColor,
_galleryFooterThemeDataControlDark.backgroundColor);
expect(imageFooterTheme.shareIconColor,
_galleryFooterThemeDataControlDark.shareIconColor);
expect(imageFooterTheme.titleTextStyle,
_galleryFooterThemeDataControlDark.titleTextStyle);
expect(imageFooterTheme.gridIconButtonColor,
_galleryFooterThemeDataControlDark.gridIconButtonColor);
expect(imageFooterTheme.bottomSheetBarrierColor,
_galleryFooterThemeDataControlDark.bottomSheetBarrierColor);
expect(imageFooterTheme.bottomSheetBackgroundColor,
_galleryFooterThemeDataControlDark.bottomSheetBackgroundColor);
expect(imageFooterTheme.bottomSheetCloseIconColor,
_galleryFooterThemeDataControlDark.bottomSheetCloseIconColor);
expect(imageFooterTheme.bottomSheetPhotosTextStyle,
_galleryFooterThemeDataControlDark.bottomSheetPhotosTextStyle);
});
}
// Light theme control
final _galleryFooterThemeDataControl = GalleryFooterThemeData(
backgroundColor: ColorTheme.light().barsBg,
shareIconColor: ColorTheme.light().textHighEmphasis,
titleTextStyle: TextTheme.light().headlineBold,
gridIconButtonColor: ColorTheme.light().textHighEmphasis,
bottomSheetBackgroundColor: ColorTheme.light().barsBg,
bottomSheetBarrierColor: ColorTheme.light().overlay,
bottomSheetCloseIconColor: ColorTheme.light().textHighEmphasis,
bottomSheetPhotosTextStyle: TextTheme.light().headlineBold,
);
// Mid-lerp theme control
const _galleryFooterThemeDataControlMidLerp = GalleryFooterThemeData(
backgroundColor: Color(0xff87898b),
shareIconColor: Color(0xff7f7f7f),
titleTextStyle: TextStyle(
color: Color(0xff7f7f7f),
fontSize: 16,
fontWeight: FontWeight.bold,
),
gridIconButtonColor: Color(0xff7f7f7f),
bottomSheetBarrierColor: Color(0x4c000000),
bottomSheetBackgroundColor: Color(0xff87898b),
bottomSheetPhotosTextStyle: TextStyle(
color: Color(0xff7f7f7f),
fontSize: 16,
fontWeight: FontWeight.bold,
),
bottomSheetCloseIconColor: Color(0xff7f7f7f),
);
// Dark theme control
final _galleryFooterThemeDataControlDark = GalleryFooterThemeData(
backgroundColor: ColorTheme.dark().barsBg,
shareIconColor: ColorTheme.dark().textHighEmphasis,
titleTextStyle: TextTheme.dark().headlineBold,
gridIconButtonColor: ColorTheme.dark().textHighEmphasis,
bottomSheetBackgroundColor: ColorTheme.dark().barsBg,
bottomSheetBarrierColor: ColorTheme.dark().overlay,
bottomSheetCloseIconColor: ColorTheme.dark().textHighEmphasis,
bottomSheetPhotosTextStyle: TextTheme.dark().headlineBold,
);
@@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
import 'package:mocktail/mocktail.dart';
class MockStreamChatClient extends Mock implements StreamChatClient {}
void main() {
test('GalleryHeaderThemeData copyWith, ==, hashCode basics', () {
expect(const GalleryHeaderThemeData(),
const GalleryHeaderThemeData().copyWith());
expect(const GalleryHeaderThemeData().hashCode,
const GalleryHeaderThemeData().copyWith().hashCode);
});
test(
'Light GalleryHeaderThemeData lerps completely to dark GalleryHeaderThemeData',
() {
expect(
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataControl,
_galleryHeaderThemeDataDarkControl, 1),
_galleryHeaderThemeDataDarkControl);
});
test(
'Light GalleryHeaderThemeData lerps halfway to dark GalleryHeaderThemeData',
() {
expect(
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataControl,
_galleryHeaderThemeDataDarkControl, 0.5),
_galleryHeaderThemeDataHalfLerpControl);
});
test(
'Dark GalleryHeaderThemeData lerps completely to light GalleryHeaderThemeData',
() {
expect(
const GalleryHeaderThemeData().lerp(_galleryHeaderThemeDataDarkControl,
_galleryHeaderThemeDataControl, 1),
_galleryHeaderThemeDataControl);
});
test('Merging dark and light themes results in a dark theme', () {
expect(
_galleryHeaderThemeDataControl
.merge(_galleryHeaderThemeDataDarkControl),
_galleryHeaderThemeDataDarkControl);
});
test('Merging dark and light themes results in a dark theme', () {
expect(
_galleryHeaderThemeDataDarkControl
.merge(_galleryHeaderThemeDataControl),
_galleryHeaderThemeDataControl);
});
testWidgets(
'Passing no GalleryHeaderThemeData returns default light theme values',
(WidgetTester tester) async {
late BuildContext _context;
await tester.pumpWidget(
MaterialApp(
builder: (context, child) => StreamChat(
client: MockStreamChatClient(),
child: child,
),
home: Builder(
builder: (context) {
_context = context;
return Scaffold(
appBar: GalleryHeader(
message: Message(),
),
);
},
),
),
);
final imageHeaderTheme = GalleryHeaderTheme.of(_context);
expect(imageHeaderTheme.closeButtonColor,
_galleryHeaderThemeDataControl.closeButtonColor);
expect(imageHeaderTheme.backgroundColor,
_galleryHeaderThemeDataControl.backgroundColor);
expect(imageHeaderTheme.iconMenuPointColor,
_galleryHeaderThemeDataControl.iconMenuPointColor);
expect(imageHeaderTheme.titleTextStyle,
_galleryHeaderThemeDataControl.titleTextStyle);
expect(imageHeaderTheme.subtitleTextStyle,
_galleryHeaderThemeDataControl.subtitleTextStyle);
expect(imageHeaderTheme.bottomSheetBarrierColor,
_galleryHeaderThemeDataControl.bottomSheetBarrierColor);
});
testWidgets(
'Passing no GalleryHeaderThemeData returns default dark theme values',
(WidgetTester tester) async {
late BuildContext _context;
await tester.pumpWidget(
MaterialApp(
builder: (context, child) => StreamChat(
client: MockStreamChatClient(),
streamChatThemeData: StreamChatThemeData.dark(),
child: child,
),
home: Builder(
builder: (context) {
_context = context;
return Scaffold(
appBar: GalleryHeader(
message: Message(),
),
);
},
),
),
);
final imageHeaderTheme = GalleryHeaderTheme.of(_context);
expect(imageHeaderTheme.closeButtonColor,
_galleryHeaderThemeDataDarkControl.closeButtonColor);
expect(imageHeaderTheme.backgroundColor,
_galleryHeaderThemeDataDarkControl.backgroundColor);
expect(imageHeaderTheme.iconMenuPointColor,
_galleryHeaderThemeDataDarkControl.iconMenuPointColor);
expect(imageHeaderTheme.titleTextStyle,
_galleryHeaderThemeDataDarkControl.titleTextStyle);
expect(imageHeaderTheme.subtitleTextStyle,
_galleryHeaderThemeDataDarkControl.subtitleTextStyle);
expect(imageHeaderTheme.bottomSheetBarrierColor,
_galleryHeaderThemeDataDarkControl.bottomSheetBarrierColor);
});
}
// Light theme test control.
final _galleryHeaderThemeDataControl = GalleryHeaderThemeData(
closeButtonColor: const Color(0xff000000),
backgroundColor: const Color(0xffffffff),
iconMenuPointColor: const Color(0xff000000),
titleTextStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.black,
),
subtitleTextStyle: const TextStyle(
fontSize: 12,
color: Colors.black,
).copyWith(
color: const Color(0xff7A7A7A),
),
bottomSheetBarrierColor: const Color.fromRGBO(0, 0, 0, 0.2),
);
// Light theme test control.
final _galleryHeaderThemeDataHalfLerpControl = GalleryHeaderThemeData(
closeButtonColor: const Color(0xff7f7f7f),
backgroundColor: const Color(0xff87898b),
iconMenuPointColor: const Color(0xff7f7f7f),
titleTextStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xff7f7f7f),
),
subtitleTextStyle: const TextStyle(
fontSize: 12,
color: Color(0xff7a7a7a),
).copyWith(
color: const Color(0xff7A7A7A),
),
bottomSheetBarrierColor: const Color(0x4c000000),
);
// Dark theme test control.
final _galleryHeaderThemeDataDarkControl = GalleryHeaderThemeData(
closeButtonColor: const Color(0xffffffff),
backgroundColor: const Color(0xff101418),
iconMenuPointColor: const Color(0xffffffff),
titleTextStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
subtitleTextStyle: const TextStyle(
fontSize: 12,
color: Colors.white,
).copyWith(
color: const Color(0xff7A7A7A),
),
bottomSheetBarrierColor: const Color.fromRGBO(0, 0, 0, 0.4),
);
@@ -37,7 +37,7 @@ void main() {
child: WillPopScope(
onWillPop: () async => false,
child: Scaffold(
body: ImageFooter(
body: GalleryFooter(
message: Message(),
),
),