reviewed filter chip layout
This commit is contained in:
parent
ccb9482221
commit
cb21761a47
9 changed files with 74 additions and 56 deletions
|
@ -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];
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
);
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue