1
0
mirror of https://github.com/flutter/samples.git synced 2026-05-19 13:28:28 +00:00

Adds veggieseasons preferences, tweaks styles + list page (#37)

This commit is contained in:
Andrew Brogdon
2018-12-14 15:05:28 -08:00
committed by GitHub
parent 60837792a1
commit 2a2c535aab
12 changed files with 368 additions and 58 deletions

View File

@@ -0,0 +1,82 @@
// 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:scoped_model/scoped_model.dart';
import 'package:veggieseasons/data/veggie.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// A model class that mirrors the options in [SettingsScreen] and stores data
/// in shared preferences.
class Preferences extends Model {
Preferences() {
_loadingFuture = _load();
}
// Keys to use with shared preferences.
static const _caloriesKey = 'calories';
static const _preferredCategoriesKey = 'preferredCategories';
// Indicates whether a call to [_load] is in progress;
Future<void> _loadingFuture;
int _desiredCalories = 2000;
Set<VeggieCategory> _preferredCategories = Set<VeggieCategory>();
Future<int> get desiredCalories async {
await _loadingFuture;
return _desiredCalories;
}
Future<Set<VeggieCategory>> get preferredCategories async {
await _loadingFuture;
return Set.from(_preferredCategories);
}
void addPreferredCategory(VeggieCategory category) {
_preferredCategories.add(category);
_save();
notifyListeners();
}
void removePreferredCategory(VeggieCategory category) {
_preferredCategories.remove(category);
_save();
notifyListeners();
}
void setDesiredCalories(int calories) {
_desiredCalories = calories;
_save();
notifyListeners();
}
Future<void> _save() async {
final prefs = await SharedPreferences.getInstance();
prefs.setInt(_caloriesKey, _desiredCalories);
// Store preferred categories as a comma-separated string containing their
// indices.
prefs.setString(_preferredCategoriesKey,
_preferredCategories.map((c) => c.index.toString()).join(','));
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
_desiredCalories = prefs.getInt(_caloriesKey) ?? 2000;
_preferredCategories.clear();
final names = prefs.getString(_preferredCategoriesKey) ?? '';
for (final name in names.split(',')) {
final index = int.parse(name) ?? 0;
if (VeggieCategory.values[index] != null) {
_preferredCategories.add(VeggieCategory.values[index]);
}
}
notifyListeners();
}
}

View File

@@ -9,13 +9,19 @@ import 'package:veggieseasons/data/model.dart';
import 'package:veggieseasons/data/veggie.dart';
import 'package:veggieseasons/styles.dart';
/// A circular widget that indicates in which seasons a particular veggie can be
/// harvested. It displays the first two letters of the season and uses a
/// different background color to represent each of the seasons as well.
/// A circular widget that represents a season of the year.
///
/// The season can be displayed as a valid harvest season or one during which a
/// particular veggie cannot be harvested. Bright colors are used in the first
/// case, and grays in the latter.
class SeasonCircle extends StatelessWidget {
SeasonCircle(this.season, this.isHarvestTime);
/// Season to be displayed by this widget.
final Season season;
SeasonCircle(this.season);
/// Whether or not [season] should be presented as a valid harvest season.
final bool isHarvestTime;
String get _firstChars {
return '${season.toString().substring(7, 8).toUpperCase()}'
@@ -28,7 +34,9 @@ class SeasonCircle extends StatelessWidget {
padding: const EdgeInsets.all(4.0),
child: DecoratedBox(
decoration: BoxDecoration(
color: Styles.seasonColors[season],
color: isHarvestTime
? Styles.seasonColors[season]
: Styles.transparentColor,
borderRadius: BorderRadius.circular(25.0),
border: Styles.seasonBorder,
),
@@ -36,7 +44,12 @@ class SeasonCircle extends StatelessWidget {
height: 50.0,
width: 50.0,
child: Center(
child: Text(_firstChars, style: Styles.seasonText),
child: Text(
_firstChars,
style: isHarvestTime
? Styles.activeSeasonText
: Styles.inactiveSeasonText,
),
),
),
),
@@ -116,8 +129,9 @@ class DetailsScreen extends StatelessWidget {
mainAxisSize: MainAxisSize.max,
children: [
Wrap(
children:
veggie.seasons.map<Widget>((s) => SeasonCircle(s)).toList(),
children: Season.values.map((s) {
return SeasonCircle(s, veggie.seasons.contains(s));
}).toList(),
),
SizedBox(width: 8.0),
Expanded(

View File

@@ -27,42 +27,44 @@ class ListScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
String dateString = DateFormat.yMMMMd("en_US").format(DateTime.now());
final model = ScopedModel.of<AppState>(context, rebuildOnChange: true);
final rows = <Widget>[];
rows.add(
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(dateString.toUpperCase(), style: Styles.minorText),
Text('In season today', style: Styles.headlineText),
],
),
),
);
rows.addAll(_generateVeggieRows(model.availableVeggies));
rows.add(
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 16.0),
child: Text('Not in season', style: Styles.headlineText),
),
);
rows.addAll(_generateVeggieRows(model.unavailableVeggies));
return CupertinoTabView(
builder: (context) => DecoratedBox(
decoration: BoxDecoration(color: Color(0xffffffff)),
child: ListView(
children: rows,
builder: (context) {
String dateString = DateFormat.yMMMMd("en_US").format(DateTime.now());
final model = ScopedModel.of<AppState>(context, rebuildOnChange: true);
final rows = <Widget>[];
rows.add(
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(dateString.toUpperCase(), style: Styles.minorText),
Text('In season today', style: Styles.headlineText),
],
),
),
);
rows.addAll(_generateVeggieRows(model.availableVeggies));
rows.add(
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 24.0, 16.0, 16.0),
child: Text('Not in season', style: Styles.headlineText),
),
);
rows.addAll(_generateVeggieRows(model.unavailableVeggies));
return DecoratedBox(
decoration: BoxDecoration(color: Color(0xffffffff)),
child: ListView(
children: rows,
),
);
},
);
}
}

View File

@@ -7,13 +7,7 @@ import 'package:flutter/widgets.dart';
import 'package:veggieseasons/data/veggie.dart';
abstract class Styles {
static const baseTextStyle = TextStyle(
color: Color.fromRGBO(10, 10, 8, 1.0),
fontFamily: 'NotoSans',
fontSize: 16.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
);
static String createHeroTag(Veggie veggie) => 'veggie_hero_${veggie.name}';
static const headlineText = TextStyle(
color: Color.fromRGBO(0, 0, 0, 0.8),
@@ -31,14 +25,6 @@ abstract class Styles {
fontWeight: FontWeight.bold,
);
static const bodyText = TextStyle(
color: Color.fromRGBO(240, 240, 240, 1.0),
fontFamily: 'NotoSans',
fontSize: 14.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
);
static const minorText = TextStyle(
color: Color.fromRGBO(128, 128, 128, 1.0),
fontFamily: 'NotoSans',
@@ -63,7 +49,7 @@ abstract class Styles {
fontWeight: FontWeight.normal,
);
static const seasonText = TextStyle(
static const activeSeasonText = TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.9),
fontFamily: 'NotoSans',
fontSize: 24.0,
@@ -71,6 +57,38 @@ abstract class Styles {
fontWeight: FontWeight.normal,
);
static const inactiveSeasonText = TextStyle(
color: Color.fromRGBO(80, 80, 80, 0.9),
fontFamily: 'NotoSans',
fontSize: 24.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
);
static const cardTitleText = TextStyle(
color: Color.fromRGBO(0, 0, 0, 0.9),
fontFamily: 'NotoSans',
fontSize: 32.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.bold,
);
static const cardCategoryText = TextStyle(
color: Color.fromRGBO(255, 255, 255, 0.9),
fontFamily: 'NotoSans',
fontSize: 16.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
);
static const cardDescriptionText = TextStyle(
color: Color.fromRGBO(0, 0, 0, 0.9),
fontFamily: 'NotoSans',
fontSize: 16.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
);
static const appBackground = Color(0xffd0d0d0);
static const scaffoldBackground = Color(0xfff0f0f0);
@@ -128,4 +146,36 @@ abstract class Styles {
end: Alignment.bottomCenter,
colors: [transparentColor, shadowColor],
);
static const Color settingsMediumGray = Color(0xffc7c7c7);
static const Color settingsItemPressed = Color(0xffd9d9d9);
static const Color settingsLineation = Color(0xffbcbbc1);
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(
0xf443,
fontFamily: CupertinoIcons.iconFont,
fontPackage: CupertinoIcons.iconFontPackage,
);
static const calorieIcon = IconData(
0xf3bb,
fontFamily: CupertinoIcons.iconFont,
fontPackage: CupertinoIcons.iconFontPackage,
);
static const checkIcon = IconData(
0xf383,
fontFamily: CupertinoIcons.iconFont,
fontPackage: CupertinoIcons.iconFontPackage,
);
}