Show arrow indicator on toolbar

When the number of items in the toolbar is very large and the toolbar
becomes scrollable, arrows are now displayed to indicate to the user
that the list is scrollable. The arrows are automatically hidden if
it is not scrollable in the respective arrow direction.
pull/245/head
Till Friebe 4 years ago
parent aba8032b8d
commit fc8546345e
  1. 15
      lib/src/widgets/toolbar.dart
  2. 125
      lib/src/widgets/toolbar/arrow_indicated_button_list.dart

@ -6,6 +6,7 @@ import 'package:image_picker/image_picker.dart';
import '../models/documents/attribute.dart';
import 'controller.dart';
import 'toolbar/arrow_indicated_button_list.dart';
import 'toolbar/clear_format_button.dart';
import 'toolbar/color_button.dart';
import 'toolbar/history_button.dart';
@ -316,21 +317,9 @@ class _QuillToolbarState extends State<QuillToolbar> {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 8),
constraints: BoxConstraints.tightFor(height: widget.preferredSize.height),
color: Theme.of(context).canvasColor,
child: CustomScrollView(
scrollDirection: Axis.horizontal,
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.children,
),
),
],
),
child: ArrowIndicatedButtonList(buttons: widget.children),
);
}
}

@ -0,0 +1,125 @@
import 'dart:async';
import 'package:flutter/material.dart';
/// Scrollable list with arrow indicators.
///
/// The arrow indicators are automatically hidden if the list is not
/// scrollable in the direction of the respective arrow.
class ArrowIndicatedButtonList extends StatefulWidget {
const ArrowIndicatedButtonList({required this.buttons, Key? key})
: super(key: key);
final List<Widget> buttons;
@override
_ArrowIndicatedButtonListState createState() =>
_ArrowIndicatedButtonListState();
}
class _ArrowIndicatedButtonListState extends State<ArrowIndicatedButtonList>
with WidgetsBindingObserver {
final ScrollController _controller = ScrollController();
bool _showLeftArrow = false;
bool _showRightArrow = false;
@override
void initState() {
super.initState();
_controller.addListener(_handleScroll);
// Listening to the WidgetsBinding instance is necessary so that we can
// hide the arrows when the window gets a new size and thus the toolbar
// becomes scrollable/unscrollable.
WidgetsBinding.instance!.addObserver(this);
// Workaround to allow the scroll controller attach to our ListView so that
// we can detect if overflow arrows need to be shown on init.
Timer.run(_handleScroll);
}
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
_buildLeftArrow(),
_buildScrollableList(),
_buildRightColor(),
],
);
}
@override
void didChangeMetrics() => _handleScroll();
@override
void dispose() {
_controller.dispose();
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
void _handleScroll() {
setState(() {
_showLeftArrow =
_controller.position.minScrollExtent != _controller.position.pixels;
_showRightArrow =
_controller.position.maxScrollExtent != _controller.position.pixels;
});
}
Widget _buildLeftArrow() {
return SizedBox(
width: 8,
child: Transform.translate(
// Move the icon a few pixels to center it
offset: const Offset(-5, 0),
child: _showLeftArrow ? const Icon(Icons.arrow_left, size: 18) : null,
),
);
}
Widget _buildScrollableList() {
return Expanded(
child: ScrollConfiguration(
// Remove the glowing effect, as we already have the arrow indicators
behavior: _NoGlowBehavior(),
// The CustomScrollView is necessary so that the children are not
// stretched to the height of the toolbar, https://bit.ly/3uC3bjI
child: CustomScrollView(
scrollDirection: Axis.horizontal,
controller: _controller,
physics: const ClampingScrollPhysics(),
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: widget.buttons,
),
)
],
),
),
);
}
Widget _buildRightColor() {
return SizedBox(
width: 8,
child: Transform.translate(
// Move the icon a few pixels to center it
offset: const Offset(-5, 0),
child: _showRightArrow ? const Icon(Icons.arrow_right, size: 18) : null,
),
);
}
}
/// ScrollBehavior without the Material glow effect.
class _NoGlowBehavior extends ScrollBehavior {
@override
Widget buildViewportChrome(BuildContext _, Widget child, AxisDirection __) {
return child;
}
}
Loading…
Cancel
Save