reviewed filter chip layout

This commit is contained in:
Thibault Deckers 2020-06-02 13:49:34 +09:00
parent ccb9482221
commit cb21761a47
9 changed files with 74 additions and 56 deletions

View file

@ -4,7 +4,8 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class FilterBar extends StatelessWidget implements PreferredSizeWidget { class FilterBar extends StatelessWidget implements PreferredSizeWidget {
static const double preferredHeight = kMinInteractiveDimension; static const double verticalPadding = 16;
static const double preferredHeight = AvesFilterChip.minChipHeight + verticalPadding;
@override @override
final Size preferredSize = const Size.fromHeight(preferredHeight); final Size preferredSize = const Size.fromHeight(preferredHeight);
@ -26,7 +27,7 @@ class FilterBar extends StatelessWidget implements PreferredSizeWidget {
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(AvesFilterChip.buttonBorderWidth / 2) + const EdgeInsets.symmetric(horizontal: 6), padding: const EdgeInsets.symmetric(horizontal: 8),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index >= filters.length) return null; if (index >= filters.length) return null;
final filter = filters[index]; final filter = filters[index];

View file

@ -17,6 +17,9 @@ class ExpandableFilterRow extends StatelessWidget {
@required this.onPressed, @required this.onPressed,
}); });
static const double horizontalPadding = 8;
static const double verticalPadding = 8;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (filters.isEmpty) return const SizedBox.shrink(); if (filters.isEmpty) return const SizedBox.shrink();
@ -48,12 +51,13 @@ class ExpandableFilterRow extends StatelessWidget {
final filtersList = filters.toList(); final filtersList = filters.toList();
final wrap = Container( final wrap = Container(
key: ValueKey('wrap$title'), key: ValueKey('wrap$title'),
padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.buttonBorderWidth / 2 + 6), padding: const EdgeInsets.symmetric(horizontal: horizontalPadding),
// specify transparent as a workaround to prevent // specify transparent as a workaround to prevent
// chip border clipping when the floating app bar is fading // chip border clipping when the floating app bar is fading
color: Colors.transparent, color: Colors.transparent,
child: Wrap( child: Wrap(
spacing: 8, spacing: horizontalPadding,
runSpacing: verticalPadding,
children: filtersList children: filtersList
.map((filter) => AvesFilterChip( .map((filter) => AvesFilterChip(
filter: filter, filter: filter,
@ -67,19 +71,17 @@ class ExpandableFilterRow extends StatelessWidget {
// specify transparent as a workaround to prevent // specify transparent as a workaround to prevent
// chip border clipping when the floating app bar is fading // chip border clipping when the floating app bar is fading
color: Colors.transparent, color: Colors.transparent,
height: kMinInteractiveDimension, height: AvesFilterChip.minChipHeight,
child: ListView.separated( child: ListView.separated(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
physics: const BouncingScrollPhysics(), physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.all(AvesFilterChip.buttonBorderWidth / 2) + const EdgeInsets.symmetric(horizontal: 6), padding: const EdgeInsets.symmetric(horizontal: horizontalPadding),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index >= filtersList.length) return null; if (index >= filtersList.length) return null;
final filter = filtersList[index]; final filter = filtersList[index];
return Center( return AvesFilterChip(
child: AvesFilterChip(
filter: filter, filter: filter,
onPressed: onPressed, onPressed: onPressed,
),
); );
}, },
separatorBuilder: (context, index) => const SizedBox(width: 8), separatorBuilder: (context, index) => const SizedBox(width: 8),

View file

@ -58,6 +58,7 @@ class ImageSearchDelegate extends SearchDelegate<CollectionFilter> {
valueListenable: expandedSectionNotifier, valueListenable: expandedSectionNotifier,
builder: (context, expandedSection, child) { builder: (context, expandedSection, child) {
return ListView( return ListView(
padding: const EdgeInsets.only(top: 8),
children: [ children: [
_buildFilterRow( _buildFilterRow(
context: context, context: context,

View file

@ -13,7 +13,8 @@ class AvesFilterChip extends StatefulWidget {
final FilterCallback onPressed; final FilterCallback onPressed;
static final BorderRadius borderRadius = BorderRadius.circular(32); static final BorderRadius borderRadius = BorderRadius.circular(32);
static const double buttonBorderWidth = 2; static const double outlineWidth = 2;
static const double minChipHeight = kMinInteractiveDimension;
static const double minChipWidth = 80; static const double minChipWidth = 80;
static const double maxChipWidth = 160; static const double maxChipWidth = 160;
static const double iconSize = 20; static const double iconSize = 20;
@ -116,49 +117,59 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
); );
} }
final shape = RoundedRectangleBorder( final borderRadius = AvesFilterChip.borderRadius;
borderRadius: AvesFilterChip.borderRadius,
);
final button = ButtonTheme( final chip = Container(
minWidth: 0,
child: Container(
constraints: const BoxConstraints( constraints: const BoxConstraints(
minWidth: AvesFilterChip.minChipWidth, minWidth: AvesFilterChip.minChipWidth,
maxWidth: AvesFilterChip.maxChipWidth, maxWidth: AvesFilterChip.maxChipWidth,
minHeight: AvesFilterChip.minChipHeight,
), ),
decoration: widget.decoration, decoration: widget.decoration,
child: Tooltip( child: Tooltip(
message: filter.tooltip, message: filter.tooltip,
preferBelow: false, preferBelow: false,
child: Material(
color: widget.decoration != null ? Colors.transparent : Theme.of(context).scaffoldBackgroundColor,
shape: RoundedRectangleBorder(
borderRadius: borderRadius,
),
child: InkWell(
onTap: widget.onPressed != null ? () => widget.onPressed(filter) : null,
borderRadius: borderRadius,
child: FutureBuilder( child: FutureBuilder(
future: _colorFuture, future: _colorFuture,
builder: (context, AsyncSnapshot<Color> snapshot) { builder: (context, AsyncSnapshot<Color> snapshot) {
return OutlineButton( final outlineColor = snapshot.hasData ? snapshot.data : Colors.transparent;
onPressed: widget.onPressed != null ? () => widget.onPressed(filter) : null, return DecoratedBox(
borderSide: BorderSide( decoration: BoxDecoration(
color: snapshot.hasData ? snapshot.data : Colors.transparent, border: Border.all(
width: AvesFilterChip.buttonBorderWidth, color: outlineColor,
width: AvesFilterChip.outlineWidth,
), ),
padding: EdgeInsets.zero, borderRadius: borderRadius,
shape: shape, ),
position: DecorationPosition.foreground,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: content, child: content,
),
); );
}, },
), ),
), ),
), ),
),
); );
return button; // TODO TLAD only hero between `FilterBar` and chips that are tapped
// TODO TLAD how to lerp between chip and grid tile return Hero(
// return Hero( tag: filter,
// tag: filter, flightShuttleBuilder: (flight, animation, direction, fromHeroContext, toHeroContext) {
// flightShuttleBuilder: (flight, animation, direction, fromHeroContext, toHeroContext) { final toHero = toHeroContext.widget as Hero;
// final toHero = toHeroContext.widget as Hero; return Center(child: toHero.child);
// return Center(content: toHero.content); },
// }, child: chip,
// content: button, );
// );
} }
} }

View file

@ -87,7 +87,7 @@ class FilterGridPage extends StatelessWidget {
slivers: [ slivers: [
appBar, appBar,
SliverPadding( SliverPadding(
padding: const EdgeInsets.all(AvesFilterChip.buttonBorderWidth), padding: const EdgeInsets.all(AvesFilterChip.outlineWidth),
sliver: SliverGrid( sliver: SliverGrid(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, i) { (context, i) {

View file

@ -57,9 +57,10 @@ class BasicSection extends StatelessWidget {
]..sort(); ]..sort();
if (filters.isEmpty) return const SizedBox.shrink(); if (filters.isEmpty) return const SizedBox.shrink();
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.buttonBorderWidth / 2) + const EdgeInsets.only(top: 8), padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.outlineWidth / 2) + const EdgeInsets.only(top: 8),
child: Wrap( child: Wrap(
spacing: 8, spacing: 8,
runSpacing: 8,
children: filters children: filters
.map((filter) => AvesFilterChip( .map((filter) => AvesFilterChip(
filter: filter, filter: filter,

View file

@ -169,7 +169,7 @@ class SectionRow extends StatelessWidget {
final buildDivider = () => const SizedBox( final buildDivider = () => const SizedBox(
width: dim, width: dim,
child: Divider( child: Divider(
thickness: AvesFilterChip.buttonBorderWidth, thickness: AvesFilterChip.outlineWidth,
color: Colors.white70, color: Colors.white70,
), ),
); );

View file

@ -110,9 +110,10 @@ class _LocationSectionState extends State<LocationSection> {
), ),
if (filters.isNotEmpty) if (filters.isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.buttonBorderWidth / 2) + const EdgeInsets.only(top: 8), padding: const EdgeInsets.symmetric(horizontal: AvesFilterChip.outlineWidth / 2) + const EdgeInsets.only(top: 8),
child: Wrap( child: Wrap(
spacing: 8, spacing: 8,
runSpacing: 8,
children: filters children: filters
.map((filter) => AvesFilterChip( .map((filter) => AvesFilterChip(
filter: filter, filter: filter,

View file

@ -36,7 +36,7 @@ class FilterTable extends StatelessWidget {
final lineHeight = 16 * textScaleFactor; final lineHeight = 16 * textScaleFactor;
return Padding( return Padding(
padding: const EdgeInsetsDirectional.only(start: AvesFilterChip.buttonBorderWidth / 2 + 6, end: 8), padding: const EdgeInsetsDirectional.only(start: AvesFilterChip.outlineWidth / 2 + 6, end: 8),
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
final showPercentIndicator = constraints.maxWidth - (chipWidth + countWidth) > percentIndicatorMinWidth; final showPercentIndicator = constraints.maxWidth - (chipWidth + countWidth) > percentIndicatorMinWidth;
@ -48,7 +48,8 @@ class FilterTable extends StatelessWidget {
final percent = count / maxCount; final percent = count / maxCount;
return TableRow( return TableRow(
children: [ children: [
Align( Container(
padding: const EdgeInsets.only(bottom: 8),
alignment: AlignmentDirectional.centerStart, alignment: AlignmentDirectional.centerStart,
child: AvesFilterChip( child: AvesFilterChip(
filter: filter, filter: filter,