Update VeggieSeasons Settings screen to use CupertinoList (#2426)

## Pre-launch Checklist

- [x] I read the [Flutter Style Guide] _recently_, and have followed its
advice.
- [x] I signed the [CLA].
- [x] I read the [Contributors Guide].
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-devrel
channel on [Discord].

<!-- Links -->
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[CLA]: https://cla.developers.google.com/
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
[Contributors Guide]:
https://github.com/flutter/samples/blob/main/CONTRIBUTING.md
pull/2427/head
Michael Thomsen 4 months ago committed by GitHub
parent 8d208d7fd4
commit 2bb837ea6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -8,8 +8,6 @@ import 'package:provider/provider.dart';
import '../data/preferences.dart'; import '../data/preferences.dart';
import '../data/veggie.dart'; import '../data/veggie.dart';
import '../styles.dart'; import '../styles.dart';
import '../widgets/settings_group.dart';
import '../widgets/settings_item.dart';
class VeggieCategorySettingsScreen extends StatelessWidget { class VeggieCategorySettingsScreen extends StatelessWidget {
const VeggieCategorySettingsScreen({super.key, this.restorationId}); const VeggieCategorySettingsScreen({super.key, this.restorationId});
@ -40,7 +38,7 @@ class VeggieCategorySettingsScreen extends StatelessWidget {
child: FutureBuilder<Set<VeggieCategory>>( child: FutureBuilder<Set<VeggieCategory>>(
future: currentPrefs, future: currentPrefs,
builder: (context, snapshot) { builder: (context, snapshot) {
final items = <SettingsItem>[]; final tiles = <CupertinoListTile>[];
for (final category in VeggieCategory.values) { for (final category in VeggieCategory.values) {
CupertinoSwitch toggle; CupertinoSwitch toggle;
@ -66,17 +64,20 @@ class VeggieCategorySettingsScreen extends StatelessWidget {
); );
} }
items.add(SettingsItem( tiles.add(
label: veggieCategoryNames[category]!, CupertinoListTile.notched(
content: toggle, title: Text(veggieCategoryNames[category]!),
)); trailing: toggle,
),
);
} }
return ListView( return ListView(
restorationId: 'list', restorationId: 'list',
children: [ children: [
SettingsGroup( CupertinoListSection.insetGrouped(
items: items, hasLeading: false,
children: tiles,
), ),
], ],
); );
@ -121,33 +122,39 @@ class CalorieSettingsScreen extends StatelessWidget {
FutureBuilder<int>( FutureBuilder<int>(
future: model.desiredCalories, future: model.desiredCalories,
builder: (context, snapshot) { builder: (context, snapshot) {
final steps = <SettingsItem>[]; final tiles = <CupertinoListTile>[];
for (var cals = max; cals < min; cals += step) { for (var cals = max; cals < min; cals += step) {
steps.add( tiles.add(
SettingsItem( CupertinoListTile.notched(
label: cals.toString(), title: Text('$cals calories'),
icon: SettingsIcon( trailing: SettingsIcon(
icon: Styles.checkIcon, icon: CupertinoIcons.check_mark,
foregroundColor: foregroundColor:
snapshot.hasData && snapshot.data == cals snapshot.hasData && snapshot.data == cals
? CupertinoColors.activeBlue ? CupertinoColors.activeBlue
: Styles.transparentColor, : Styles.transparentColor,
backgroundColor: Styles.transparentColor, backgroundColor: Styles.transparentColor,
), ),
onPress: snapshot.hasData onTap: snapshot.hasData
? () => model.setDesiredCalories(cals) ? () => model.setDesiredCalories(cals)
: null, : null,
), ),
); );
} }
return SettingsGroup( return CupertinoListSection.insetGrouped(
items: steps, header: Text(
header: const SettingsGroupHeader('Available calorie levels'), 'Available calorie levels'.toUpperCase(),
footer: style: Styles.settingsGroupHeaderText(
const SettingsGroupFooter('These are used for serving ' CupertinoTheme.of(context)),
'calculations'), ),
footer: Text(
'These are used for serving calculations',
style: Styles.settingsGroupFooterText(
CupertinoTheme.of(context)),
),
children: tiles,
); );
}, },
), ),
@ -168,59 +175,50 @@ class SettingsScreen extends StatefulWidget {
} }
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
SettingsItem _buildCaloriesItem(BuildContext context, Preferences prefs) { CupertinoListTile _buildCaloriesTile(
return SettingsItem( BuildContext context, Preferences prefs) {
label: 'Calorie Target', return CupertinoListTile.notched(
icon: const SettingsIcon( leading: const SettingsIcon(
backgroundColor: Styles.iconBlue, backgroundColor: CupertinoColors.systemBlue,
icon: Styles.calorieIcon, icon: Styles.calorieIcon,
), ),
content: FutureBuilder<int>( title: const Text('Calorie Target'),
additionalInfo: FutureBuilder<int>(
future: prefs.desiredCalories, future: prefs.desiredCalories,
builder: (context, snapshot) { builder: (context, snapshot) {
return Row( return Text(
children: [ snapshot.data?.toString() ?? '',
Text( style: CupertinoTheme.of(context).textTheme.textStyle,
snapshot.data?.toString() ?? '',
style: CupertinoTheme.of(context).textTheme.textStyle,
),
const SizedBox(width: 8),
const SettingsNavigationIndicator(),
],
); );
}, },
), ),
onPress: () { trailing: const CupertinoListTileChevron(),
context.go('/settings/calories'); onTap: () => context.go('/settings/calories'),
},
); );
} }
SettingsItem _buildCategoriesItem(BuildContext context, Preferences prefs) { CupertinoListTile _buildCategoriesTile(
return SettingsItem( BuildContext context, Preferences prefs) {
label: 'Preferred Categories', return CupertinoListTile.notched(
subtitle: 'What types of veggies you prefer!', leading: const SettingsIcon(
icon: const SettingsIcon( backgroundColor: CupertinoColors.systemOrange,
backgroundColor: Styles.iconGold,
icon: Styles.preferenceIcon, icon: Styles.preferenceIcon,
), ),
content: const SettingsNavigationIndicator(), title: const Text('Preferred Categories'),
onPress: () { trailing: const CupertinoListTileChevron(),
context.go('/settings/categories'); onTap: () => context.go('/settings/categories'),
},
); );
} }
SettingsItem _buildRestoreDefaultsItem( CupertinoListTile _buildRestoreDefaultsTile(
BuildContext context, Preferences prefs) { BuildContext context, Preferences prefs) {
return SettingsItem( return CupertinoListTile.notched(
label: 'Restore Defaults', leading: const SettingsIcon(
icon: const SettingsIcon(
backgroundColor: CupertinoColors.systemRed, backgroundColor: CupertinoColors.systemRed,
icon: Styles.resetIcon, icon: Styles.resetIcon,
), ),
content: const SettingsNavigationIndicator(), title: const Text('Restore Defaults'),
onPress: () { onTap: () {
showCupertinoDialog<void>( showCupertinoDialog<void>(
context: context, context: context,
builder: (context) => CupertinoAlertDialog( builder: (context) => CupertinoAlertDialog(
@ -254,36 +252,61 @@ class _SettingsScreenState extends State<SettingsScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final prefs = Provider.of<Preferences>(context); final prefs = Provider.of<Preferences>(context);
return RestorationScope( return CupertinoPageScaffold(
restorationId: widget.restorationId, backgroundColor:
child: CupertinoPageScaffold( Styles.scaffoldBackground(CupertinoTheme.brightnessOf(context)),
child: Container( child: CustomScrollView(
color: slivers: <Widget>[
Styles.scaffoldBackground(CupertinoTheme.brightnessOf(context)), const CupertinoSliverNavigationBar(
child: CustomScrollView( largeTitle: Text('Settings'),
restorationId: 'list', ),
slivers: [ SliverList(
const CupertinoSliverNavigationBar( delegate: SliverChildListDelegate(
largeTitle: Text('Settings'), [
), CupertinoListSection.insetGrouped(
SliverSafeArea( children: [
top: false, _buildCaloriesTile(context, prefs),
sliver: SliverList( _buildCategoriesTile(context, prefs),
delegate: SliverChildListDelegate( ],
[
SettingsGroup(
items: [
_buildCaloriesItem(context, prefs),
_buildCategoriesItem(context, prefs),
_buildRestoreDefaultsItem(context, prefs),
],
),
],
),
), ),
), CupertinoListSection.insetGrouped(
], children: [
_buildRestoreDefaultsTile(context, prefs),
],
),
],
),
), ),
],
),
);
}
}
class SettingsIcon extends StatelessWidget {
const SettingsIcon({
required this.icon,
this.foregroundColor = CupertinoColors.white,
this.backgroundColor = CupertinoColors.black,
super.key,
});
final Color backgroundColor;
final Color foregroundColor;
final IconData icon;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: backgroundColor,
),
child: Center(
child: Icon(
icon,
color: foregroundColor,
size: 20,
), ),
), ),
); );

@ -107,7 +107,7 @@ abstract class Styles {
static TextStyle settingsGroupFooterText(CupertinoThemeData themeData) => static TextStyle settingsGroupFooterText(CupertinoThemeData themeData) =>
themeData.textTheme.textStyle.copyWith( themeData.textTheme.textStyle.copyWith(
color: Styles.settingsGroupSubtitle, color: const Color(0xff777777),
fontSize: 13, fontSize: 13,
letterSpacing: -0.08, letterSpacing: -0.08,
); );
@ -116,7 +116,7 @@ abstract class Styles {
static Color? scaffoldBackground(Brightness brightness) => static Color? scaffoldBackground(Brightness brightness) =>
brightness == Brightness.light brightness == Brightness.light
? CupertinoColors.lightBackgroundGray ? CupertinoColors.extraLightBackgroundGray
: null; : null;
static const frostedBackground = Color(0xccf8f8f8); static const frostedBackground = Color(0xccf8f8f8);
@ -203,12 +203,6 @@ abstract class Styles {
static const Color settingsBackground = Color(0xffefeff4); static const Color settingsBackground = Color(0xffefeff4);
static const Color settingsGroupSubtitle = Color(0xff777777);
static const Color iconBlue = Color(0xff0000ff);
static const Color iconGold = Color(0xffdba800);
static const preferenceIcon = IconData( static const preferenceIcon = IconData(
0xf443, 0xf443,
fontFamily: CupertinoIcons.iconFont, fontFamily: CupertinoIcons.iconFont,
@ -227,12 +221,6 @@ abstract class Styles {
fontPackage: CupertinoIcons.iconFontPackage, fontPackage: CupertinoIcons.iconFontPackage,
); );
static const checkIcon = IconData(
0xf383,
fontFamily: CupertinoIcons.iconFont,
fontPackage: CupertinoIcons.iconFontPackage,
);
static const servingInfoBorderColor = Color(0xffb0b0b0); static const servingInfoBorderColor = Color(0xffb0b0b0);
static const ColorFilter desaturatedColorFilter = static const ColorFilter desaturatedColorFilter =

@ -1,111 +0,0 @@
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import '../styles.dart';
import 'settings_item.dart';
// The widgets in this file present a group of Cupertino-style settings items to
// the user. In the future, the Cupertino package in the Flutter SDK will
// include dedicated widgets for this purpose, but for now they're done here.
//
// See https://github.com/flutter/flutter/projects/29 for more info.
class SettingsGroupHeader extends StatelessWidget {
const SettingsGroupHeader(this.title, {super.key});
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
left: 15,
right: 15,
bottom: 6,
),
child: Text(
title.toUpperCase(),
style: Styles.settingsGroupHeaderText(CupertinoTheme.of(context)),
),
);
}
}
class SettingsGroupFooter extends StatelessWidget {
const SettingsGroupFooter(this.title, {super.key});
final String title;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
left: 15,
right: 15,
top: 7.5,
),
child: Text(title,
style: Styles.settingsGroupFooterText(CupertinoTheme.of(context))),
);
}
}
class SettingsGroup extends StatelessWidget {
SettingsGroup({
required this.items,
this.header,
this.footer,
super.key,
}) : assert(items.isNotEmpty);
final List<SettingsItem> items;
final Widget? header;
final Widget? footer;
@override
Widget build(BuildContext context) {
var brightness = CupertinoTheme.brightnessOf(context);
final dividedItems = <Widget>[items[0]];
for (var i = 1; i < items.length; i++) {
dividedItems.add(Container(
color: Styles.settingsLineation(brightness),
height: 0.3,
));
dividedItems.add(items[i]);
}
return Padding(
padding: EdgeInsets.only(
top: header == null ? 35 : 22,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (header != null) header!,
Container(
decoration: BoxDecoration(
color: CupertinoColors.white,
border: Border(
top: BorderSide(
color: Styles.settingsLineation(brightness),
width: 0,
),
bottom: BorderSide(
color: Styles.settingsLineation(brightness),
width: 0,
),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: dividedItems,
),
),
if (footer != null) footer!,
],
),
);
}
}

@ -1,161 +0,0 @@
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/cupertino.dart';
import '../styles.dart';
// The widgets in this file present a Cupertino-style settings item to the user.
// In the future, the Cupertino package in the Flutter SDK will include
// dedicated widgets for this purpose, but for now they're done here.
//
// See https://github.com/flutter/flutter/projects/29 for more info.
typedef SettingsItemCallback = FutureOr<void> Function();
class SettingsNavigationIndicator extends StatelessWidget {
const SettingsNavigationIndicator({super.key});
@override
Widget build(BuildContext context) {
return const Icon(
CupertinoIcons.forward,
color: Styles.settingsMediumGray,
size: 21,
);
}
}
class SettingsIcon extends StatelessWidget {
const SettingsIcon({
required this.icon,
this.foregroundColor = CupertinoColors.white,
this.backgroundColor = CupertinoColors.black,
super.key,
});
final Color backgroundColor;
final Color foregroundColor;
final IconData icon;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: backgroundColor,
),
child: Center(
child: Icon(
icon,
color: foregroundColor,
size: 20,
),
),
);
}
}
class SettingsItem extends StatefulWidget {
const SettingsItem({
required this.label,
this.icon,
this.content,
this.subtitle,
this.onPress,
super.key,
});
final String label;
final Widget? icon;
final Widget? content;
final String? subtitle;
final SettingsItemCallback? onPress;
@override
State<SettingsItem> createState() => _SettingsItemState();
}
class _SettingsItemState extends State<SettingsItem> {
bool pressed = false;
@override
Widget build(BuildContext context) {
var themeData = CupertinoTheme.of(context);
var brightness = CupertinoTheme.brightnessOf(context);
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
color: Styles.settingsItemColor(brightness),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
if (widget.onPress != null) {
setState(() {
pressed = true;
});
await widget.onPress!();
Future.delayed(
const Duration(milliseconds: 150),
() {
setState(() {
pressed = false;
});
},
);
}
},
child: SizedBox(
height: widget.subtitle == null ? 44 : 57,
child: Row(
children: [
if (widget.icon != null)
Padding(
padding: const EdgeInsets.only(
left: 15,
bottom: 2,
),
child: SizedBox(
height: 29,
width: 29,
child: widget.icon,
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 15,
),
child: widget.subtitle != null
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8.5),
Text(widget.label,
style: themeData.textTheme.textStyle),
const SizedBox(height: 4),
Text(
widget.subtitle!,
style: Styles.settingsItemSubtitleText(themeData),
),
],
)
: Padding(
padding: const EdgeInsets.only(top: 1.5),
child: Text(widget.label,
style: themeData.textTheme.textStyle),
),
),
),
Padding(
padding: const EdgeInsets.only(right: 11),
child: widget.content ?? Container(),
),
],
),
),
),
);
}
}
Loading…
Cancel
Save