Show arrow indicator on toolbar (#245)
parent
aba8032b8d
commit
16d6f243b8
2 changed files with 127 additions and 13 deletions
@ -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…
Reference in new issue