added time-to-take-action 3s option;

use stretch over bouncing overscroll;
fixed time shift wheel scroll bar
This commit is contained in:
Thibault Deckers 2022-05-23 12:34:41 +09:00
parent 512c507942
commit 44ed934a8c
8 changed files with 65 additions and 46 deletions

View file

@ -10,6 +10,8 @@ extension ExtraAccessibilityTimeout on AccessibilityTimeout {
return context.l10n.settingsSystemDefault; return context.l10n.settingsSystemDefault;
case AccessibilityTimeout.appDefault: case AccessibilityTimeout.appDefault:
return context.l10n.settingsDefault; return context.l10n.settingsDefault;
case AccessibilityTimeout.s3:
return context.l10n.timeSeconds(3);
case AccessibilityTimeout.s10: case AccessibilityTimeout.s10:
return context.l10n.timeSeconds(10); return context.l10n.timeSeconds(10);
case AccessibilityTimeout.s30: case AccessibilityTimeout.s30:

View file

@ -1,6 +1,6 @@
enum AccessibilityAnimations { system, disabled, enabled } enum AccessibilityAnimations { system, disabled, enabled }
enum AccessibilityTimeout { system, appDefault, s10, s30, s60, s120 } enum AccessibilityTimeout { system, appDefault, s3, s10, s30, s60, s120 }
enum AvesThemeColorMode { monochrome, polychrome } enum AvesThemeColorMode { monochrome, polychrome }

View file

@ -81,14 +81,13 @@ class _FilterBarState extends State<FilterBar> {
color: Colors.transparent, color: Colors.transparent,
height: FilterBar.preferredHeight, height: FilterBar.preferredHeight,
child: NotificationListener<ScrollNotification>( child: NotificationListener<ScrollNotification>(
// cancel notification bubbling so that the draggable scrollbar // cancel notification bubbling so that the draggable scroll bar
// does not misinterpret filter bar scrolling for collection scrolling // does not misinterpret filter bar scrolling for collection scrolling
onNotification: (notification) => true, onNotification: (notification) => true,
child: AnimatedList( child: AnimatedList(
key: _animatedListKey, key: _animatedListKey,
initialItemCount: widget.filters.length, initialItemCount: widget.filters.length,
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 4),
itemBuilder: (context, index, animation) { itemBuilder: (context, index, animation) {
if (index >= widget.filters.length) return const SizedBox(); if (index >= widget.filters.length) return const SizedBox();

View file

@ -105,6 +105,8 @@ mixin FeedbackMixin {
return Duration(milliseconds: millis); return Duration(milliseconds: millis);
case AccessibilityTimeout.appDefault: case AccessibilityTimeout.appDefault:
return appDefaultDuration; return appDefaultDuration;
case AccessibilityTimeout.s3:
return const Duration(seconds: 3);
case AccessibilityTimeout.s10: case AccessibilityTimeout.s10:
return const Duration(seconds: 10); return const Duration(seconds: 10);
case AccessibilityTimeout.s30: case AccessibilityTimeout.s30:

View file

@ -41,39 +41,51 @@ class _WheelSelectorState<T> extends State<WheelSelector<T>> {
const background = Colors.transparent; const background = Colors.transparent;
final foreground = DefaultTextStyle.of(context).style.color!; final foreground = DefaultTextStyle.of(context).style.color!;
return Padding( return NotificationListener<ScrollNotification>(
padding: const EdgeInsets.all(8), // cancel notification bubbling so that the dialog scroll bar
child: SizedBox( // does not misinterpret wheel scrolling for dialog content scrolling
width: itemSize.width, onNotification: (notification) => true,
height: itemSize.height * 3, child: Padding(
child: ShaderMask( padding: const EdgeInsets.all(8),
shaderCallback: LinearGradient( child: SizedBox(
begin: Alignment.topCenter, width: itemSize.width,
end: Alignment.bottomCenter, height: itemSize.height * 3,
colors: [ child: ShaderMask(
background, shaderCallback: LinearGradient(
foreground, begin: Alignment.topCenter,
foreground, end: Alignment.bottomCenter,
background, colors: [
], background,
).createShader, foreground,
child: ListWheelScrollView( foreground,
controller: _controller, background,
physics: const FixedExtentScrollPhysics(parent: BouncingScrollPhysics()), ],
diameterRatio: 1.2, ).createShader,
itemExtent: itemSize.height, child: Theme(
squeeze: 1.3, data: Theme.of(context).copyWith(
onSelectedItemChanged: (i) => valueNotifier.value = values[i], scrollbarTheme: ScrollbarThemeData(
children: values thumbVisibility: MaterialStateProperty.all(false),
.map((i) => SizedBox.fromSize( ),
size: itemSize, ),
child: Text( child: ListWheelScrollView(
'$i', controller: _controller,
textAlign: widget.textAlign, physics: const FixedExtentScrollPhysics(parent: BouncingScrollPhysics()),
style: widget.textStyle, diameterRatio: 1.2,
), itemExtent: itemSize.height,
)) squeeze: 1.3,
.toList(), onSelectedItemChanged: (i) => valueNotifier.value = values[i],
children: values
.map((i) => SizedBox.fromSize(
size: itemSize,
child: Text(
'$i',
textAlign: widget.textAlign,
style: widget.textStyle,
),
))
.toList(),
),
),
), ),
), ),
), ),

View file

@ -78,7 +78,6 @@ class ExpandableFilterRow extends StatelessWidget {
height: AvesFilterChip.minChipHeight, height: AvesFilterChip.minChipHeight,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: horizontalPadding), padding: const EdgeInsets.symmetric(horizontal: horizontalPadding),
itemBuilder: (context, index) { itemBuilder: (context, index) {
return index < filterList.length ? _buildFilterChip(filterList[index]) : const SizedBox(); return index < filterList.length ? _buildFilterChip(filterList[index]) : const SizedBox();

View file

@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
class AvesDialog extends StatelessWidget { class AvesDialog extends StatelessWidget {
final String? title; final String? title;
final ScrollController? scrollController; final ScrollController scrollController;
final List<Widget>? scrollableContent; final List<Widget>? scrollableContent;
final bool hasScrollBar; final bool hasScrollBar;
final double horizontalContentPadding; final double horizontalContentPadding;
@ -16,16 +16,17 @@ class AvesDialog extends StatelessWidget {
static const double controlCaptionPadding = 16; static const double controlCaptionPadding = 16;
static const double borderWidth = 1.0; static const double borderWidth = 1.0;
const AvesDialog({ AvesDialog({
super.key, super.key,
this.title, this.title,
this.scrollController, ScrollController? scrollController,
this.scrollableContent, this.scrollableContent,
this.hasScrollBar = true, this.hasScrollBar = true,
this.horizontalContentPadding = defaultHorizontalContentPadding, this.horizontalContentPadding = defaultHorizontalContentPadding,
this.content, this.content,
required this.actions, required this.actions,
}) : assert((scrollableContent != null) ^ (content != null)); }) : assert((scrollableContent != null) ^ (content != null)),
scrollController = scrollController ?? ScrollController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -56,10 +57,8 @@ class AvesDialog extends StatelessWidget {
} }
if (scrollableContent != null) { if (scrollableContent != null) {
final _scrollController = scrollController ?? ScrollController();
Widget child = ListView( Widget child = ListView(
controller: _scrollController, controller: scrollController,
shrinkWrap: true, shrinkWrap: true,
children: scrollableContent!, children: scrollableContent!,
); );
@ -76,7 +75,14 @@ class AvesDialog extends StatelessWidget {
), ),
), ),
child: Scrollbar( child: Scrollbar(
controller: _scrollController, controller: scrollController,
notificationPredicate: (notification) {
// as of Flutter v3.0.1, the `Scrollbar` does not only respond to the nearest `ScrollView`
// despite the `defaultScrollNotificationPredicate` checking notification depth,
// as the notifications coming from the controller in `ListWheelScrollView` in `WheelSelector` still have a depth of 0.
// Cancelling notification bubbling seems ineffective, so we check the metrics type as a workaround.
return defaultScrollNotificationPredicate(notification) && notification.metrics is! FixedExtentMetrics;
},
child: child, child: child,
), ),
); );

View file

@ -54,7 +54,6 @@ class _CrumbLineState extends State<CrumbLine> {
child: ListView.builder( child: ListView.builder(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
controller: _controller, controller: _controller,
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 8), padding: const EdgeInsets.symmetric(horizontal: 8),
itemBuilder: (context, index) { itemBuilder: (context, index) {
Widget _buildText(String text) => Padding( Widget _buildText(String text) => Padding(