Skip to content

Commit d395b43

Browse files
committed
Playground: Features: BottomSheet Clip behavior. TabBar indicator animation.
1 parent 95ae525 commit d395b43

File tree

6 files changed

+220
-35
lines changed

6 files changed

+220
-35
lines changed

CHANGELOG.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
All changes to the **FlexColorScheme** (FCS) package are documented here.
44

5-
## 8.1.0 WIP
5+
## 8.1.0
66

77
**Dec 28, 2024**
88

99
### Package
1010

11-
12-
* First prep for Flutter 3.27.0 update with new Color features that supports wide gamut. In total, 350 deprecations will have to be replaced with the new Color features.
11+
* Updated the package to support and require at least Flutter v3.27.0.
12+
* Fixed all new analyzer lint warnings and removed usage of all deprecated `Color` properties.
1313

1414
**NEW**
1515

@@ -39,7 +39,9 @@ All changes to the **FlexColorScheme** (FCS) package are documented here.
3939
**NEW**
4040

4141
* **Slider**: On the Slider panel added a scheme color selector for the Slider thumb color.
42-
42+
* **BottomSheet**: On the BottomSheet panel added a clipping behavior selector.
43+
* **TabBar**: On the TabBar panel added a tab indicator animation selector.
44+
4345
**CHANGE**
4446

4547
* **Slider**: Increased Playground allowed max Slider track height from 14 to 40.

example/lib/example5_themes_playground/utils/import_export_playground_settings.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum JsonKeys {
2626
playgroundVersion(key: 'playground_version'),
2727
typeColor(key: 'color'),
2828
typeEnumAdaptiveResponse(key: 'enum_adaptive_response'),
29+
typeEnumClip(key: 'enum_clip'),
2930
typeEnumFlexAppBarStyle(key: 'enum_flex_appbar_style'),
3031
typeEnumFlexFixedColorStyle(key: 'enum_flex_fixed_color_style'),
3132
typeEnumFlexInputBorderType(key: 'enum_flex_input_border_type'),
@@ -48,6 +49,7 @@ enum JsonKeys {
4849
typeEnumShowValueIndicator(key: 'enum_show_value_indicator'),
4950
typeEnumSplashType(key: 'enum_splash_type'),
5051
typeEnumTabAlignment(key: 'enum_tab_alignment'),
52+
typeEnumTabBarIndicatorAnimation(key: 'enum_tabbar_indicator_animation'),
5153
typeEnumTabBarIndicatorSize(key: 'enum_tabbar_indicator_size'),
5254
typeEnumThemeModeColor(key: 'enum_theme_mode'),
5355
typeEnumVisualDensity(key: 'enum_visual_density'),
@@ -86,6 +88,8 @@ Future<String> exportPlaygroundSettings(ThemeController controller) async {
8688
String? dartType;
8789
if (object is AdaptiveResponse) {
8890
dartType = JsonKeys.typeEnumAdaptiveResponse.key;
91+
} else if (object is Clip) {
92+
dartType = JsonKeys.typeEnumClip.key;
8993
} else if (object is FlexAppBarStyle) {
9094
dartType = JsonKeys.typeEnumFlexAppBarStyle.key;
9195
} else if (object is FlexFixedColorStyle) {
@@ -128,6 +132,8 @@ Future<String> exportPlaygroundSettings(ThemeController controller) async {
128132
dartType = JsonKeys.typeEnumSplashType.key;
129133
} else if (object is TabAlignment) {
130134
dartType = JsonKeys.typeEnumTabAlignment.key;
135+
} else if (object is TabIndicatorAnimation) {
136+
dartType = JsonKeys.typeEnumTabBarIndicatorAnimation.key;
131137
} else if (object is TabBarIndicatorSize) {
132138
dartType = JsonKeys.typeEnumTabBarIndicatorSize.key;
133139
} else if (object is ThemeMode) {

example/lib/example5_themes_playground/widgets/panels/bottom_sheet/bottom_sheet_panel.dart

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,41 @@
11
import 'package:flutter/material.dart';
22

33
import '../../../../shared/controllers/theme_controller.dart';
4+
import '../../../../shared/utils/link_text_span.dart';
45
import '../../../../shared/widgets/universal/list_tile_reveal.dart';
56
import '../../../../shared/widgets/universal/showcase_material.dart';
67
import '../../../../shared/widgets/universal/slider_list_tile_reveal.dart';
78
import '../../../theme/theme_values.dart';
89
import '../../shared/color_scheme_popup_menu.dart';
10+
import '../../shared/enum_popup_menu.dart';
911

1012
class BottomSheetPanel extends StatelessWidget {
1113
const BottomSheetPanel(this.controller, {super.key});
1214

1315
final ThemeController controller;
1416

17+
static final Uri _fcsIssue270 = Uri(
18+
scheme: 'https',
19+
host: 'github.com',
20+
path: 'rydmike/flex_color_scheme/issues/270',
21+
);
22+
23+
static final Uri _fcsFlutterIssue = Uri(
24+
scheme: 'https',
25+
host: 'github.com',
26+
path: 'flutter/flutter/issues/',
27+
);
28+
1529
@override
1630
Widget build(BuildContext context) {
1731
final ThemeData theme = Theme.of(context);
1832
final bool useMaterial3 = theme.useMaterial3;
33+
final TextStyle spanTextStyle = theme.textTheme.bodySmall!
34+
.copyWith(color: theme.colorScheme.onSurfaceVariant);
35+
final TextStyle linkStyle = theme.textTheme.bodySmall!.copyWith(
36+
color: theme.colorScheme.primary,
37+
fontWeight: FontWeight.bold,
38+
);
1939

2040
// The most common logic for enabling Playground controls.
2141
final bool enableControl =
@@ -56,6 +76,17 @@ class BottomSheetPanel extends StatelessWidget {
5676
valueDefaultLabel: sheetRadiusDefaultLabel,
5777
valueDefaultDisabledLabel: useMaterial3 ? '28 dp' : '0 dp',
5878
),
79+
EnumPopupMenu<Clip>(
80+
enabled: enableControl,
81+
values: Clip.values,
82+
title: const Text('Content clip behavior'),
83+
subtitleReveal: const Text('Controls how the content is clipped '
84+
'inside the BottomSheet. Clip is needed if you have content '
85+
'in the bottom sheet that would overlap its rounded top '
86+
'corners. See known issues below for more info.\n'),
87+
value: controller.bottomSheetClipBehavior,
88+
onChanged: controller.setBottomSheetClipBehavior,
89+
),
5990
const Divider(),
6091
const ListTile(title: Text('Standard BottomSheet')),
6192
const SizedBox(height: 8),
@@ -113,6 +144,49 @@ class BottomSheetPanel extends StatelessWidget {
113144
value: controller.bottomSheetModalSchemeColor,
114145
onChanged: controller.setBottomSheetModalSchemeColor,
115146
),
147+
const Divider(),
148+
ListTileReveal(
149+
dense: true,
150+
title: const Text('Known issues'),
151+
subtitleReveal: RichText(
152+
text: TextSpan(
153+
children: <TextSpan>[
154+
TextSpan(
155+
style: spanTextStyle,
156+
text: 'Using a BackdropFilter with a BottomSheet does not '
157+
'work if any other content clip behavior than Clip.none '
158+
'is used. This is a known issue in Flutter, see '
159+
'Flutter SDK ',
160+
),
161+
LinkTextSpan(
162+
style: linkStyle,
163+
uri: _fcsFlutterIssue,
164+
text: 'issue #XXXXX',
165+
),
166+
// _fcsChipUmbrellaIssue115364
167+
TextSpan(
168+
style: spanTextStyle,
169+
text: '.\n\n'
170+
'The issues is also reported in the FlexColorScheme '
171+
'issue tracker, see issue ',
172+
),
173+
LinkTextSpan(
174+
style: linkStyle,
175+
uri: _fcsIssue270,
176+
text: 'issue #270',
177+
),
178+
TextSpan(
179+
style: spanTextStyle,
180+
text: '. It contains a work around example that can be used '
181+
'to change the clip behavior to Clip.none, if you need '
182+
'to use a BackdropFilter with a BottomSheet, with FCS '
183+
'versions prior to 8.1.0, when the property value '
184+
'defaulted to Clip.antiAlias and could not be changed.\n',
185+
),
186+
],
187+
),
188+
),
189+
),
116190
],
117191
);
118192
}

example/lib/example5_themes_playground/widgets/panels/tab_bar/tab_bar_panel.dart

Lines changed: 65 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,24 @@ class TabBarPanel extends StatelessWidget {
124124
return 'TabBarStyle';
125125
}
126126

127+
// Logic for default unselected light mode default label
128+
String defaultIndicatorAnimation() {
129+
if (!controller.useSubThemes ||
130+
!controller.useFlexColorScheme ||
131+
controller.tabBarIndicatorSize == null) {
132+
if (useMaterial3) {
133+
return 'Default (elastic)';
134+
} else {
135+
return 'Default (linear)';
136+
}
137+
}
138+
if (controller.tabBarIndicatorSize == TabBarIndicatorSize.label) {
139+
return 'Default (elastic)';
140+
} else {
141+
return 'Default (linear)';
142+
}
143+
}
144+
127145
return Column(
128146
children: <Widget>[
129147
const SizedBox(height: 8),
@@ -166,32 +184,53 @@ class TabBarPanel extends StatelessWidget {
166184
value: controller.tabBarStyle,
167185
onChanged: controller.setTabBarStyle,
168186
),
169-
EnumPopupMenu<TabAlignment>(
170-
enabled: enableControl,
171-
values: TabAlignment.values,
172-
title: const Text('Tab alignment'),
173-
subtitleReveal: const Text('If any of your TabBar widgets are '
174-
'scrollable only the TabBar alignment options "fill" and '
175-
'"center" are valid values.\n'
176-
'If any of your TabBar widgets are fixed, the common case, '
177-
'only the TabBar alignment options "start", "startOffset" and '
178-
'"center" are valid alignment values.\n'
179-
'\n'
180-
'If you theme to an alignment value that is not '
181-
"valid by any of your TabBar widget's scrolling setting, those "
182-
'TabBars will throw an exception !!!\n'
183-
'\n'
184-
'If you have both scrollable and fixed TabBar widgets '
185-
'in your app, you can only theme to the "center" alignment or '
186-
'leave it at default undefined, which causes Flutter to use '
187-
'different default styles for fixed and scrollable variants.\n'
188-
'\n'
189-
'NOTE: The TabBar widgets presented in the Playground have '
190-
'logic to ignore invalid TabAlignment values.\n'
191-
'Hot take: Flutter should do this by default and not throw!\n'),
192-
value: controller.tabBarTabAlignment,
193-
onChanged: controller.setTabBarTabAlignment,
194-
),
187+
ResponsiveTwoWidgets(builder: (BuildContext context, bool isRow) {
188+
return RowOrColumn(
189+
firstWidget: EnumPopupMenu<TabAlignment>(
190+
enabled: enableControl,
191+
values: TabAlignment.values,
192+
title: const Text('Tab alignment'),
193+
subtitleReveal: const Text(
194+
'ISSUE:\n'
195+
'If any of your TabBar widgets are '
196+
'scrollable, only the TabBar alignment options "fill" and '
197+
'"center" are valid values.\n'
198+
'If any of your TabBar widgets are fixed, the common case, '
199+
'only the TabBar alignment options "start", "startOffset" and '
200+
'"center" are valid alignment values.\n'
201+
'\n'
202+
'If you theme to an alignment value that is not '
203+
"valid by any of your TabBar widget's scrolling setting, those "
204+
'TabBars will throw an exception !!!\n'
205+
'\n'
206+
'If you have both scrollable and fixed TabBar widgets '
207+
'in your app, you can only theme to the "center" alignment or '
208+
'leave it at default undefined, which causes Flutter to use '
209+
'different default styles for fixed and scrollable variants.\n'
210+
'\n'
211+
'NOTE: The TabBar widgets presented in the Playground have '
212+
'logic to ignore invalid TabAlignment values.\n'
213+
'Hot take: Flutter should do this by default and not throw!\n',
214+
),
215+
value: controller.tabBarTabAlignment,
216+
onChanged: controller.setTabBarTabAlignment,
217+
),
218+
lastWidget: EnumPopupMenu<TabIndicatorAnimation>(
219+
enabled: enableControl,
220+
values: TabIndicatorAnimation.values,
221+
defaultLabel: defaultIndicatorAnimation(),
222+
title: const Text('Tab animation'),
223+
subtitleReveal: const Text('Default is elastic if style is only '
224+
'label, otherwise linear.\n\n'
225+
'ISSUE:\n'
226+
'In Flutter 3.27 the tab indicator animation style elastic '
227+
'does not seem to work, the style is identical to linear.\n'),
228+
value: controller.tabBarIndicatorAnimation,
229+
onChanged: controller.setTabBarIndicatorAnimation,
230+
),
231+
isRow: isRow,
232+
);
233+
}),
195234
if (isLight) ...<Widget>[
196235
ColorSchemePopupMenu(
197236
enabled: enableControl,

example/lib/example5_themes_playground/widgets/shared/enum_popup_menu.dart

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,16 @@ class EnumPopupMenu<T extends Enum> extends StatelessWidget {
315315
}
316316
}
317317
}
318+
if (T == TabIndicatorAnimation) {
319+
switch (value) {
320+
case TabIndicatorAnimation.linear:
321+
return 'Linear animation';
322+
case TabIndicatorAnimation.elastic:
323+
return 'Elastic animation';
324+
case null:
325+
return 'Default';
326+
}
327+
}
318328
if (T == ShowValueIndicator) {
319329
switch (value) {
320330
case ShowValueIndicator.onlyForDiscrete:
@@ -424,6 +434,20 @@ class EnumPopupMenu<T extends Enum> extends StatelessWidget {
424434
return 'Default\nM:padded, D:shrinkWrap';
425435
}
426436
}
437+
if (T == Clip) {
438+
switch (value) {
439+
case Clip.none:
440+
return 'No clip';
441+
case Clip.hardEdge:
442+
return 'Clip, no anti-aliasing';
443+
case Clip.antiAlias:
444+
return 'Clip with anti-aliasing';
445+
case Clip.antiAliasWithSaveLayer:
446+
return 'Clip with anti-aliasing and saveLayer';
447+
case null:
448+
return 'Default (no clip)';
449+
}
450+
}
427451
if (T == AdaptiveResponse) {
428452
final AdaptiveResponse? castValue = value as AdaptiveResponse?;
429453
return castValue?.label ?? 'Default (${AdaptiveResponse.off.label})';
@@ -479,11 +503,27 @@ class EnumPopupMenu<T extends Enum> extends StatelessWidget {
479503
),
480504
Tooltip(
481505
message: 'Width equals label',
482-
child: Icon(Icons.format_underlined_outlined),
506+
child: Icon(Icons.border_bottom_outlined),
483507
),
484508
Tooltip(
485509
message: 'Width equals entire tab',
486-
child: Icon(Icons.border_bottom_outlined),
510+
child: Icon(Icons.format_underlined_outlined),
511+
),
512+
];
513+
}
514+
if (T == TabIndicatorAnimation) {
515+
return const <Widget>[
516+
Tooltip(
517+
message: 'Default',
518+
child: Icon(Icons.texture_outlined),
519+
),
520+
Tooltip(
521+
message: 'Linear move to next tab',
522+
child: Icon(Icons.horizontal_rule_outlined),
523+
),
524+
Tooltip(
525+
message: 'Elastic stretch to next tab',
526+
child: Icon(Icons.linear_scale_outlined),
487527
),
488528
];
489529
}
@@ -652,6 +692,30 @@ class EnumPopupMenu<T extends Enum> extends StatelessWidget {
652692
),
653693
];
654694
}
695+
if (T == Clip) {
696+
return const <Widget>[
697+
Tooltip(
698+
message: 'Default (None, no clip at all)',
699+
child: Icon(Icons.texture_outlined),
700+
),
701+
Tooltip(
702+
message: 'None, no clip at all',
703+
child: Icon(Icons.settings_overscan_outlined),
704+
),
705+
Tooltip(
706+
message: 'Clip, no anti-aliasing.',
707+
child: Icon(Icons.border_clear_outlined),
708+
),
709+
Tooltip(
710+
message: 'Clip with anti-aliasing',
711+
child: Icon(Icons.border_style_outlined),
712+
),
713+
Tooltip(
714+
message: 'Clip with anti-aliasing and saveLayer',
715+
child: Icon(Icons.rounded_corner_outlined),
716+
),
717+
];
718+
}
655719
if (T == AdaptiveResponse) {
656720
return <Widget>[
657721
Tooltip(

example/lib/shared/const/app.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ abstract final class App {
5959
static const String icon = 'assets/images/app_icon.png';
6060
// URL for Netlify hosting build.
6161
// This will be the only one later when WASM works OK.
62-
static const String playgroundURL = 'https://playground.flexcolorscheme.com/';
62+
//static const String playgroundURL='https://playground.flexcolorscheme.com/';
6363
// URL for GitHub pages build.
64-
// static const String playgroundURL =
65-
// 'https://rydmike.com/flexcolorscheme/themesplayground-latest/';
64+
static const String playgroundURL =
65+
'https://rydmike.com/flexcolorscheme/themesplayground-latest/';
6666

6767
static final Uri packageUri = Uri(
6868
scheme: 'https',

0 commit comments

Comments
 (0)