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

View file

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

View file

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

View file

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

View file

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

View file

@ -57,9 +57,10 @@ class BasicSection extends StatelessWidget {
]..sort();
if (filters.isEmpty) return const SizedBox.shrink();
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(
spacing: 8,
runSpacing: 8,
children: filters
.map((filter) => AvesFilterChip(
filter: filter,

View file

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

View file

@ -110,9 +110,10 @@ class _LocationSectionState extends State<LocationSection> {
),
if (filters.isNotEmpty)
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(
spacing: 8,
runSpacing: 8,
children: filters
.map((filter) => AvesFilterChip(
filter: filter,

View file

@ -36,7 +36,7 @@ class FilterTable extends StatelessWidget {
final lineHeight = 16 * textScaleFactor;
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(
builder: (context, constraints) {
final showPercentIndicator = constraints.maxWidth - (chipWidth + countWidth) > percentIndicatorMinWidth;
@ -48,7 +48,8 @@ class FilterTable extends StatelessWidget {
final percent = count / maxCount;
return TableRow(
children: [
Align(
Container(
padding: const EdgeInsets.only(bottom: 8),
alignment: AlignmentDirectional.centerStart,
child: AvesFilterChip(
filter: filter,