mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 13:58:47 +00:00
Dart 3.9 / Flutter 3.35 [first LLM release] (#2714)
I got carried away with Gemini and basically rewrote CI and the release process for the new LLM reality. This work was largely completed by Gemini. - Bump all SDK versions to the current beta (3.9.0-0) - Run `flutter channel beta` - Wrote `ci_script.dart` to replace the bash scripts - Converted repository to pub workspace #2499 - Added llm.md and release.md - Added redirect for deprecated Samples Index ## 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 have added sample code updates to the [changelog]. - [x] I updated/added relevant documentation (doc comments with `///`).
This commit is contained in:
@@ -15,7 +15,9 @@ class AppState extends ChangeNotifier {
|
||||
|
||||
List<Veggie> get availableVeggies {
|
||||
var currentSeason = _getSeasonForDate(DateTime.now());
|
||||
return _veggies.where((v) => v.seasons.contains(currentSeason)).toList();
|
||||
return _veggies
|
||||
.where((v) => v.seasons.contains(currentSeason))
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<Veggie> get favoriteVeggies =>
|
||||
@@ -23,15 +25,16 @@ class AppState extends ChangeNotifier {
|
||||
|
||||
List<Veggie> get unavailableVeggies {
|
||||
var currentSeason = _getSeasonForDate(DateTime.now());
|
||||
return _veggies.where((v) => !v.seasons.contains(currentSeason)).toList();
|
||||
return _veggies
|
||||
.where((v) => !v.seasons.contains(currentSeason))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Veggie getVeggie(int? id) => _veggies.singleWhere((v) => v.id == id);
|
||||
|
||||
List<Veggie> searchVeggies(String? terms) =>
|
||||
_veggies
|
||||
.where((v) => v.name.toLowerCase().contains(terms!.toLowerCase()))
|
||||
.toList();
|
||||
List<Veggie> searchVeggies(String? terms) => _veggies
|
||||
.where((v) => v.name.toLowerCase().contains(terms!.toLowerCase()))
|
||||
.toList();
|
||||
|
||||
void setFavorite(int? id, bool isFavorite) {
|
||||
var veggie = getVeggie(id);
|
||||
@@ -75,7 +78,9 @@ class AppState extends ChangeNotifier {
|
||||
case 12:
|
||||
return date.day < 22 ? Season.autumn : Season.winter;
|
||||
default:
|
||||
throw ArgumentError('Can\'t return a season for month #${date.month}.');
|
||||
throw ArgumentError(
|
||||
'Can\'t return a season for month #${date.month}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,15 @@ class LocalVeggieProvider {
|
||||
name: 'Apples',
|
||||
imageAssetPath: 'assets/images/apple.jpg',
|
||||
category: VeggieCategory.fruit,
|
||||
shortDescription: 'Green or red, they\'re generally round and tasty.',
|
||||
shortDescription:
|
||||
'Green or red, they\'re generally round and tasty.',
|
||||
accentColor: const Color(0x40de8c66),
|
||||
seasons: [Season.winter, Season.spring, Season.summer, Season.autumn],
|
||||
seasons: [
|
||||
Season.winter,
|
||||
Season.spring,
|
||||
Season.summer,
|
||||
Season.autumn,
|
||||
],
|
||||
vitaminAPercentage: 2,
|
||||
vitaminCPercentage: 8,
|
||||
servingSize: 'One large apple',
|
||||
@@ -75,16 +81,24 @@ class LocalVeggieProvider {
|
||||
'Root',
|
||||
'Seed',
|
||||
], 0),
|
||||
Trivia('"Jerusalem artichoke" is another term for which vegetable?', [
|
||||
'Potato',
|
||||
'Cabbage',
|
||||
'Sunchoke',
|
||||
], 2),
|
||||
Trivia('Which city claims to be The Artichoke Capital of the World?', [
|
||||
'Castroville, California',
|
||||
'Galveston, Texas',
|
||||
'London, England',
|
||||
], 0),
|
||||
Trivia(
|
||||
'"Jerusalem artichoke" is another term for which vegetable?',
|
||||
[
|
||||
'Potato',
|
||||
'Cabbage',
|
||||
'Sunchoke',
|
||||
],
|
||||
2,
|
||||
),
|
||||
Trivia(
|
||||
'Which city claims to be The Artichoke Capital of the World?',
|
||||
[
|
||||
'Castroville, California',
|
||||
'Galveston, Texas',
|
||||
'London, England',
|
||||
],
|
||||
0,
|
||||
),
|
||||
Trivia('Artichokes are technically which type of plant?', [
|
||||
'Thistle',
|
||||
'Azalea',
|
||||
@@ -97,7 +111,8 @@ class LocalVeggieProvider {
|
||||
name: 'Asparagus',
|
||||
imageAssetPath: 'assets/images/asparagus.jpg',
|
||||
category: VeggieCategory.fern,
|
||||
shortDescription: 'It\'s been used a food and medicine for millenia.',
|
||||
shortDescription:
|
||||
'It\'s been used a food and medicine for millenia.',
|
||||
accentColor: const Color(0x408cb437),
|
||||
seasons: [Season.spring],
|
||||
vitaminAPercentage: 10,
|
||||
@@ -132,7 +147,8 @@ class LocalVeggieProvider {
|
||||
name: 'Avocado',
|
||||
imageAssetPath: 'assets/images/avocado.jpg',
|
||||
category: VeggieCategory.stealthFruit,
|
||||
shortDescription: 'One of the oiliest, richest fruits money can buy.',
|
||||
shortDescription:
|
||||
'One of the oiliest, richest fruits money can buy.',
|
||||
accentColor: const Color(0x40b0ba59),
|
||||
seasons: [Season.winter, Season.spring, Season.summer],
|
||||
vitaminAPercentage: 0,
|
||||
@@ -167,7 +183,8 @@ class LocalVeggieProvider {
|
||||
name: 'Blackberries',
|
||||
imageAssetPath: 'assets/images/blackberry.jpg',
|
||||
category: VeggieCategory.berry,
|
||||
shortDescription: 'Find them on backroads and fences in the Northwest.',
|
||||
shortDescription:
|
||||
'Find them on backroads and fences in the Northwest.',
|
||||
accentColor: const Color(0x409d5adb),
|
||||
seasons: [Season.summer],
|
||||
vitaminAPercentage: 6,
|
||||
@@ -255,11 +272,15 @@ class LocalVeggieProvider {
|
||||
['Mustard greens', 'Apples', 'Potatoes'],
|
||||
0,
|
||||
),
|
||||
Trivia('Cauliflower\'s green spiral-shaped cousin is known as what?', [
|
||||
'Romesco',
|
||||
'Brittany cabbage',
|
||||
'Muscle sprouts',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Cauliflower\'s green spiral-shaped cousin is known as what?',
|
||||
[
|
||||
'Romesco',
|
||||
'Brittany cabbage',
|
||||
'Muscle sprouts',
|
||||
],
|
||||
0,
|
||||
),
|
||||
Trivia('Green cauliflower is sometimes called what?', [
|
||||
'Broccoflower',
|
||||
'Avocadoflower',
|
||||
@@ -290,11 +311,15 @@ class LocalVeggieProvider {
|
||||
'By rhizome',
|
||||
'By packing up and moving to Des Moines',
|
||||
], 0),
|
||||
Trivia('Some farmers cover their endive with shade to reduce what?', [
|
||||
'Size',
|
||||
'Toughness',
|
||||
'Bitterness',
|
||||
], 2),
|
||||
Trivia(
|
||||
'Some farmers cover their endive with shade to reduce what?',
|
||||
[
|
||||
'Size',
|
||||
'Toughness',
|
||||
'Bitterness',
|
||||
],
|
||||
2,
|
||||
),
|
||||
],
|
||||
),
|
||||
Veggie(
|
||||
@@ -385,11 +410,15 @@ class LocalVeggieProvider {
|
||||
'They\'re grown in the shade',
|
||||
'They\'re a distinct species',
|
||||
], 0),
|
||||
Trivia('How quickly can a green pepper grow from seed to harvest?', [
|
||||
'10 weeks',
|
||||
'20 weeks',
|
||||
'30 weeks',
|
||||
], 0),
|
||||
Trivia(
|
||||
'How quickly can a green pepper grow from seed to harvest?',
|
||||
[
|
||||
'10 weeks',
|
||||
'20 weeks',
|
||||
'30 weeks',
|
||||
],
|
||||
0,
|
||||
),
|
||||
],
|
||||
),
|
||||
Veggie(
|
||||
@@ -427,7 +456,8 @@ class LocalVeggieProvider {
|
||||
name: 'Kale',
|
||||
imageAssetPath: 'assets/images/kale.jpg',
|
||||
category: VeggieCategory.cruciferous,
|
||||
shortDescription: 'The meanest vegetable. Does not want to be eaten.',
|
||||
shortDescription:
|
||||
'The meanest vegetable. Does not want to be eaten.',
|
||||
accentColor: const Color(0x40a86bd8),
|
||||
seasons: [Season.winter, Season.autumn],
|
||||
vitaminAPercentage: 133,
|
||||
@@ -440,11 +470,15 @@ class LocalVeggieProvider {
|
||||
'The first frost',
|
||||
'Reading it a sad story',
|
||||
], 1),
|
||||
Trivia('Which of these isn\'t a color in which Kale can be found?', [
|
||||
'Purple',
|
||||
'White',
|
||||
'Orange',
|
||||
], 2),
|
||||
Trivia(
|
||||
'Which of these isn\'t a color in which Kale can be found?',
|
||||
[
|
||||
'Purple',
|
||||
'White',
|
||||
'Orange',
|
||||
],
|
||||
2,
|
||||
),
|
||||
Trivia(
|
||||
'One serving of kale provides what percentage of a typical person\'s requirement for vitamin K?',
|
||||
['100%', '300%', '900%'],
|
||||
@@ -522,7 +556,8 @@ class LocalVeggieProvider {
|
||||
name: 'Limes',
|
||||
imageAssetPath: 'assets/images/lime.jpg',
|
||||
category: VeggieCategory.citrus,
|
||||
shortDescription: 'Couldn\'t have ceviche and margaritas without them.',
|
||||
shortDescription:
|
||||
'Couldn\'t have ceviche and margaritas without them.',
|
||||
accentColor: const Color(0x4089b733),
|
||||
seasons: [Season.winter],
|
||||
vitaminAPercentage: 0,
|
||||
@@ -552,7 +587,8 @@ class LocalVeggieProvider {
|
||||
name: 'Mangos',
|
||||
imageAssetPath: 'assets/images/mango.jpg',
|
||||
category: VeggieCategory.tropical,
|
||||
shortDescription: 'A fun orange fruit popular with smoothie enthusiasts.',
|
||||
shortDescription:
|
||||
'A fun orange fruit popular with smoothie enthusiasts.',
|
||||
accentColor: const Color(0x40fcc93c),
|
||||
seasons: [Season.summer, Season.autumn],
|
||||
vitaminAPercentage: 72,
|
||||
@@ -629,21 +665,29 @@ class LocalVeggieProvider {
|
||||
servingSize: '1 medium nectarine',
|
||||
caloriesPerServing: 60,
|
||||
trivia: const [
|
||||
Trivia('Nectarines are technically a variety of which other fruit?', [
|
||||
'Peach',
|
||||
'Plum',
|
||||
'Cherry',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Nectarines are technically a variety of which other fruit?',
|
||||
[
|
||||
'Peach',
|
||||
'Plum',
|
||||
'Cherry',
|
||||
],
|
||||
0,
|
||||
),
|
||||
Trivia('Nectarines are sometimes called what?', [
|
||||
'Neckless geese',
|
||||
'Giant grapes',
|
||||
'Shaved peaches',
|
||||
], 2),
|
||||
Trivia('Nectarines are thought to have originated in which country?', [
|
||||
'China',
|
||||
'Italy',
|
||||
'Ethiopia',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Nectarines are thought to have originated in which country?',
|
||||
[
|
||||
'China',
|
||||
'Italy',
|
||||
'Ethiopia',
|
||||
],
|
||||
0,
|
||||
),
|
||||
],
|
||||
),
|
||||
Veggie(
|
||||
@@ -651,7 +695,8 @@ class LocalVeggieProvider {
|
||||
name: 'Persimmons',
|
||||
imageAssetPath: 'assets/images/persimmon.jpg',
|
||||
category: VeggieCategory.fruit,
|
||||
shortDescription: 'It\'s like a plum and an apple had a baby together.',
|
||||
shortDescription:
|
||||
'It\'s like a plum and an apple had a baby together.',
|
||||
accentColor: const Color(0x40979852),
|
||||
seasons: [Season.winter, Season.autumn],
|
||||
vitaminAPercentage: 0,
|
||||
@@ -719,11 +764,15 @@ class LocalVeggieProvider {
|
||||
servingSize: '1 medium spud',
|
||||
caloriesPerServing: 110,
|
||||
trivia: const [
|
||||
Trivia('Which country consumes the most fried potatoes per capita?', [
|
||||
'United States',
|
||||
'Belgium',
|
||||
'Ireland',
|
||||
], 1),
|
||||
Trivia(
|
||||
'Which country consumes the most fried potatoes per capita?',
|
||||
[
|
||||
'United States',
|
||||
'Belgium',
|
||||
'Ireland',
|
||||
],
|
||||
1,
|
||||
),
|
||||
Trivia(
|
||||
'Who is credited with introducing French Fries to the United States?',
|
||||
['Thomas Jefferson', 'Betsy Ross', 'Alexander Hamilton'],
|
||||
@@ -741,7 +790,8 @@ class LocalVeggieProvider {
|
||||
name: 'Radicchio',
|
||||
imageAssetPath: 'assets/images/radicchio.jpg',
|
||||
category: VeggieCategory.leafy,
|
||||
shortDescription: 'It\'s that bitter taste in the salad you\'re eating.',
|
||||
shortDescription:
|
||||
'It\'s that bitter taste in the salad you\'re eating.',
|
||||
accentColor: const Color(0x40d75875),
|
||||
seasons: [Season.spring, Season.autumn],
|
||||
vitaminAPercentage: 0,
|
||||
@@ -749,11 +799,15 @@ class LocalVeggieProvider {
|
||||
servingSize: '2 cups shredded',
|
||||
caloriesPerServing: 20,
|
||||
trivia: const [
|
||||
Trivia('Radicchio is a particuarly good source of which mineral?', [
|
||||
'Manganese',
|
||||
'Mercury',
|
||||
'Molybdenum',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Radicchio is a particuarly good source of which mineral?',
|
||||
[
|
||||
'Manganese',
|
||||
'Mercury',
|
||||
'Molybdenum',
|
||||
],
|
||||
0,
|
||||
),
|
||||
Trivia('Radicchio should be stored at what temperature?', [
|
||||
'Room temperature',
|
||||
'Refrigerator temperature',
|
||||
@@ -775,7 +829,8 @@ class LocalVeggieProvider {
|
||||
name: 'Radishes',
|
||||
imageAssetPath: 'assets/images/radish.jpg',
|
||||
category: VeggieCategory.root,
|
||||
shortDescription: 'Try roasting them in addition to slicing them up raw.',
|
||||
shortDescription:
|
||||
'Try roasting them in addition to slicing them up raw.',
|
||||
accentColor: const Color(0x40819e4e),
|
||||
seasons: [Season.spring, Season.autumn],
|
||||
vitaminAPercentage: 0,
|
||||
@@ -783,11 +838,15 @@ class LocalVeggieProvider {
|
||||
servingSize: '7 radishes',
|
||||
caloriesPerServing: 10,
|
||||
trivia: const [
|
||||
Trivia('Which ancient civilization is known to have used radish oil?', [
|
||||
'Egyptian',
|
||||
'Sumerian',
|
||||
'Incan',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Which ancient civilization is known to have used radish oil?',
|
||||
[
|
||||
'Egyptian',
|
||||
'Sumerian',
|
||||
'Incan',
|
||||
],
|
||||
0,
|
||||
),
|
||||
Trivia(
|
||||
'What\'s the name of the radish commonly used in Japanese cuisine?',
|
||||
['Daisuki', 'Daijin', 'Daikon'],
|
||||
@@ -805,7 +864,8 @@ class LocalVeggieProvider {
|
||||
name: 'Squash',
|
||||
imageAssetPath: 'assets/images/squash.jpg',
|
||||
category: VeggieCategory.gourd,
|
||||
shortDescription: 'Just slather them in butter and pop \'em in the oven.',
|
||||
shortDescription:
|
||||
'Just slather them in butter and pop \'em in the oven.',
|
||||
accentColor: const Color(0x40dbb721),
|
||||
seasons: [Season.winter, Season.autumn],
|
||||
vitaminAPercentage: 297,
|
||||
@@ -823,11 +883,15 @@ class LocalVeggieProvider {
|
||||
'Furniture',
|
||||
'Musical instruments',
|
||||
], 0),
|
||||
Trivia('Which country is the world\'s largest importer of squashes?', [
|
||||
'China',
|
||||
'United States',
|
||||
'Russia',
|
||||
], 1),
|
||||
Trivia(
|
||||
'Which country is the world\'s largest importer of squashes?',
|
||||
[
|
||||
'China',
|
||||
'United States',
|
||||
'Russia',
|
||||
],
|
||||
1,
|
||||
),
|
||||
],
|
||||
),
|
||||
Veggie(
|
||||
@@ -849,11 +913,15 @@ class LocalVeggieProvider {
|
||||
'100',
|
||||
'200',
|
||||
], 2),
|
||||
Trivia('Strawberries are closely related to which type of flower?', [
|
||||
'The rose',
|
||||
'The daisy',
|
||||
'The tulip',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Strawberries are closely related to which type of flower?',
|
||||
[
|
||||
'The rose',
|
||||
'The daisy',
|
||||
'The tulip',
|
||||
],
|
||||
0,
|
||||
),
|
||||
Trivia('Strawberries are unique among fruit for what reason?', [
|
||||
'Their seeds are on the outside',
|
||||
'Their flowers are striped',
|
||||
@@ -866,7 +934,8 @@ class LocalVeggieProvider {
|
||||
name: 'Tangelo',
|
||||
imageAssetPath: 'assets/images/tangelo.jpg',
|
||||
category: VeggieCategory.citrus,
|
||||
shortDescription: 'No one\'s sure what they are or where they came from.',
|
||||
shortDescription:
|
||||
'No one\'s sure what they are or where they came from.',
|
||||
accentColor: const Color(0x40f88c06),
|
||||
seasons: [Season.winter, Season.autumn],
|
||||
vitaminAPercentage: 6,
|
||||
@@ -904,21 +973,29 @@ class LocalVeggieProvider {
|
||||
servingSize: '1 medium tomato',
|
||||
caloriesPerServing: 25,
|
||||
trivia: const [
|
||||
Trivia('French speakers sometimes refer to tomatoes with which name?', [
|
||||
'Piet de terre',
|
||||
'Mille-feuille',
|
||||
'Pomme d\'amour',
|
||||
], 2),
|
||||
Trivia(
|
||||
'French speakers sometimes refer to tomatoes with which name?',
|
||||
[
|
||||
'Piet de terre',
|
||||
'Mille-feuille',
|
||||
'Pomme d\'amour',
|
||||
],
|
||||
2,
|
||||
),
|
||||
Trivia(
|
||||
'The largest tomato known to have been grown weighed in at how many pounds?',
|
||||
['8', '10', '12'],
|
||||
0,
|
||||
),
|
||||
Trivia('Which country is the world\'s largest producer of tomatoes?', [
|
||||
'China',
|
||||
'Italy',
|
||||
'Ecuador',
|
||||
], 0),
|
||||
Trivia(
|
||||
'Which country is the world\'s largest producer of tomatoes?',
|
||||
[
|
||||
'China',
|
||||
'Italy',
|
||||
'Ecuador',
|
||||
],
|
||||
0,
|
||||
),
|
||||
],
|
||||
),
|
||||
Veggie(
|
||||
@@ -934,7 +1011,11 @@ class LocalVeggieProvider {
|
||||
servingSize: '2 cups diced',
|
||||
caloriesPerServing: 80,
|
||||
trivia: const [
|
||||
Trivia('How much of a watermelon is water?', ['50%', '75%', '90%'], 2),
|
||||
Trivia('How much of a watermelon is water?', [
|
||||
'50%',
|
||||
'75%',
|
||||
'90%',
|
||||
], 2),
|
||||
Trivia(
|
||||
'Which nation is famous for growing watermelons in unsual shapes like cubes and hearts?',
|
||||
['Armenia', 'Japan', 'Saudi Arabia'],
|
||||
@@ -977,7 +1058,11 @@ class LocalVeggieProvider {
|
||||
),
|
||||
Trivia(
|
||||
'Who is generally credited with giving bell peppers their peppery name?',
|
||||
['Christopher Columbus', 'Benjamin Franklin', 'Eleanor Roosevelt'],
|
||||
[
|
||||
'Christopher Columbus',
|
||||
'Benjamin Franklin',
|
||||
'Eleanor Roosevelt',
|
||||
],
|
||||
0,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -6,7 +6,8 @@ import 'dart:io' show Platform;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter/services.dart' show DeviceOrientation, SystemChrome;
|
||||
import 'package:flutter/services.dart'
|
||||
show DeviceOrientation, SystemChrome;
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:window_size/window_size.dart';
|
||||
@@ -30,14 +31,17 @@ void main() {
|
||||
]);
|
||||
setupWindow();
|
||||
|
||||
runApp(const RootRestorationScope(restorationId: 'root', child: VeggieApp()));
|
||||
runApp(
|
||||
const RootRestorationScope(restorationId: 'root', child: VeggieApp()),
|
||||
);
|
||||
}
|
||||
|
||||
const double windowWidth = 480;
|
||||
const double windowHeight = 854;
|
||||
|
||||
void setupWindow() {
|
||||
if (!kIsWeb && (Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||
if (!kIsWeb &&
|
||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)) {
|
||||
setWindowTitle('Veggie Seasons');
|
||||
setWindowMinSize(const Size(windowWidth, windowHeight));
|
||||
setWindowMaxSize(const Size(windowWidth, windowHeight));
|
||||
@@ -143,7 +147,9 @@ class _VeggieAppState extends State<VeggieApp> with RestorationMixin {
|
||||
return VeggieSeasonsPage(
|
||||
key: state.pageKey,
|
||||
restorationId: 'route.favorites',
|
||||
child: const FavoritesScreen(restorationId: 'favorites'),
|
||||
child: const FavoritesScreen(
|
||||
restorationId: 'favorites',
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: [_buildDetailsRoute()],
|
||||
@@ -165,7 +171,9 @@ class _VeggieAppState extends State<VeggieApp> with RestorationMixin {
|
||||
return VeggieSeasonsPage(
|
||||
key: state.pageKey,
|
||||
restorationId: 'route.settings',
|
||||
child: const SettingsScreen(restorationId: 'settings'),
|
||||
child: const SettingsScreen(
|
||||
restorationId: 'settings',
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: [
|
||||
|
||||
@@ -21,7 +21,10 @@ class ServingInfoChart extends StatelessWidget {
|
||||
// Creates a [Text] widget to display a veggie's "percentage of your daily
|
||||
// value of this vitamin" data adjusted for the user's preferred calorie
|
||||
// target.
|
||||
Widget _buildVitaminText(int standardPercentage, Future<int> targetCalories) {
|
||||
Widget _buildVitaminText(
|
||||
int standardPercentage,
|
||||
Future<int> targetCalories,
|
||||
) {
|
||||
return FutureBuilder<int>(
|
||||
future: targetCalories,
|
||||
builder: (context, snapshot) {
|
||||
@@ -62,7 +65,10 @@ class ServingInfoChart extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text('Calories', style: Styles.detailsServingLabelText(themeData)),
|
||||
Text(
|
||||
'Calories',
|
||||
style: Styles.detailsServingLabelText(themeData),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${veggie.caloriesPerServing} kCal',
|
||||
@@ -75,18 +81,30 @@ class ServingInfoChart extends StatelessWidget {
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text('Vitamin A', style: Styles.detailsServingLabelText(themeData)),
|
||||
Text(
|
||||
'Vitamin A',
|
||||
style: Styles.detailsServingLabelText(themeData),
|
||||
),
|
||||
const Spacer(),
|
||||
_buildVitaminText(veggie.vitaminAPercentage, prefs.desiredCalories),
|
||||
_buildVitaminText(
|
||||
veggie.vitaminAPercentage,
|
||||
prefs.desiredCalories,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Text('Vitamin C', style: Styles.detailsServingLabelText(themeData)),
|
||||
Text(
|
||||
'Vitamin C',
|
||||
style: Styles.detailsServingLabelText(themeData),
|
||||
),
|
||||
const Spacer(),
|
||||
_buildVitaminText(veggie.vitaminCPercentage, prefs.desiredCalories),
|
||||
_buildVitaminText(
|
||||
veggie.vitaminCPercentage,
|
||||
prefs.desiredCalories,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
@@ -131,7 +149,10 @@ class InfoView extends StatelessWidget {
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('Seasons', style: Styles.detailsServingLabelText(themeData)),
|
||||
Text(
|
||||
'Seasons',
|
||||
style: Styles.detailsServingLabelText(themeData),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
@@ -143,10 +164,9 @@ class InfoView extends StatelessWidget {
|
||||
children: [
|
||||
Icon(
|
||||
Styles.seasonIconData[season],
|
||||
color:
|
||||
veggie.seasons.contains(season)
|
||||
? Styles.seasonColors[season]
|
||||
: const Color.fromRGBO(128, 128, 128, 1),
|
||||
color: veggie.seasons.contains(season)
|
||||
? Styles.seasonColors[season]
|
||||
: const Color.fromRGBO(128, 128, 128, 1),
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
|
||||
@@ -25,26 +25,32 @@ class FavoritesScreen extends StatelessWidget {
|
||||
middle: Text('My Garden'),
|
||||
),
|
||||
child: Center(
|
||||
child:
|
||||
model.favoriteVeggies.isEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Text(
|
||||
'You haven\'t added any favorite veggies to your garden yet.',
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
)
|
||||
: ListView(
|
||||
restorationId: 'list',
|
||||
children: [
|
||||
const SizedBox(height: 24),
|
||||
for (Veggie veggie in model.favoriteVeggies)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 24),
|
||||
child: VeggieHeadline(veggie),
|
||||
),
|
||||
],
|
||||
child: model.favoriteVeggies.isEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Text(
|
||||
'You haven\'t added any favorite veggies to your garden yet.',
|
||||
style: CupertinoTheme.of(
|
||||
context,
|
||||
).textTheme.textStyle,
|
||||
),
|
||||
)
|
||||
: ListView(
|
||||
restorationId: 'list',
|
||||
children: [
|
||||
const SizedBox(height: 24),
|
||||
for (Veggie veggie in model.favoriteVeggies)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
16,
|
||||
0,
|
||||
16,
|
||||
24,
|
||||
),
|
||||
child: VeggieHeadline(veggie),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -21,7 +21,10 @@ class HomeScreen extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final index = _getSelectedIndex(GoRouter.of(context).location);
|
||||
final String location = GoRouter.of(
|
||||
context,
|
||||
).routerDelegate.currentConfiguration.uri.toString();
|
||||
final index = _getSelectedIndex(location);
|
||||
return RestorationScope(
|
||||
restorationId: restorationId,
|
||||
child: CupertinoPageScaffold(
|
||||
|
||||
@@ -27,7 +27,11 @@ class ListScreen extends StatelessWidget {
|
||||
future: prefs.preferredCategories,
|
||||
builder: (context, snapshot) {
|
||||
final data = snapshot.data ?? <VeggieCategory>{};
|
||||
return VeggieCard(veggie, inSeason, data.contains(veggie.category));
|
||||
return VeggieCard(
|
||||
veggie,
|
||||
inSeason,
|
||||
data.contains(veggie.category),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -18,7 +18,8 @@ class SearchScreen extends StatefulWidget {
|
||||
State<SearchScreen> createState() => _SearchScreenState();
|
||||
}
|
||||
|
||||
class _SearchScreenState extends State<SearchScreen> with RestorationMixin {
|
||||
class _SearchScreenState extends State<SearchScreen>
|
||||
with RestorationMixin {
|
||||
final controller = RestorableTextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
String? terms;
|
||||
@@ -87,7 +88,11 @@ class _SearchScreenState extends State<SearchScreen> with RestorationMixin {
|
||||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16, bottom: 24),
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
bottom: 24,
|
||||
),
|
||||
child: VeggieHeadline(veggies[i - 1]),
|
||||
);
|
||||
}
|
||||
@@ -106,7 +111,9 @@ class _SearchScreenState extends State<SearchScreen> with RestorationMixin {
|
||||
builder: (context) {
|
||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||
value: SystemUiOverlayStyle(
|
||||
statusBarBrightness: MediaQuery.platformBrightnessOf(context),
|
||||
statusBarBrightness: MediaQuery.platformBrightnessOf(
|
||||
context,
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
|
||||
@@ -58,7 +58,10 @@ class VeggieCategorySettingsScreen extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
} else {
|
||||
toggle = const CupertinoSwitch(value: false, onChanged: null);
|
||||
toggle = const CupertinoSwitch(
|
||||
value: false,
|
||||
onChanged: null,
|
||||
);
|
||||
}
|
||||
|
||||
tiles.add(
|
||||
@@ -129,14 +132,13 @@ class CalorieSettingsScreen extends StatelessWidget {
|
||||
icon: CupertinoIcons.check_mark,
|
||||
foregroundColor:
|
||||
snapshot.hasData && snapshot.data == cals
|
||||
? CupertinoColors.activeBlue
|
||||
: Styles.transparentColor,
|
||||
? CupertinoColors.activeBlue
|
||||
: Styles.transparentColor,
|
||||
backgroundColor: Styles.transparentColor,
|
||||
),
|
||||
onTap:
|
||||
snapshot.hasData
|
||||
? () => model.setDesiredCalories(cals)
|
||||
: null,
|
||||
onTap: snapshot.hasData
|
||||
? () => model.setDesiredCalories(cals)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -227,29 +229,28 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
onTap: () {
|
||||
showCupertinoDialog<void>(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => CupertinoAlertDialog(
|
||||
title: const Text('Are you sure?'),
|
||||
content: const Text(
|
||||
'Are you sure you want to reset the current settings?',
|
||||
),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
child: const Text('Yes'),
|
||||
onPressed: () async {
|
||||
await prefs.restoreDefaults();
|
||||
if (!context.mounted) return;
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
child: const Text('No'),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
],
|
||||
builder: (context) => CupertinoAlertDialog(
|
||||
title: const Text('Are you sure?'),
|
||||
content: const Text(
|
||||
'Are you sure you want to reset the current settings?',
|
||||
),
|
||||
actions: [
|
||||
CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
child: const Text('Yes'),
|
||||
onPressed: () async {
|
||||
await prefs.restoreDefaults();
|
||||
if (!context.mounted) return;
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
CupertinoDialogAction(
|
||||
isDefaultAction: true,
|
||||
child: const Text('No'),
|
||||
onPressed: () => context.pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -43,35 +43,39 @@ abstract class Styles {
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
|
||||
static TextStyle cardCategoryText(CupertinoThemeData themeData) => themeData
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(color: const Color.fromRGBO(255, 255, 255, 0.9));
|
||||
static TextStyle cardCategoryText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(
|
||||
color: const Color.fromRGBO(255, 255, 255, 0.9),
|
||||
);
|
||||
|
||||
static TextStyle cardDescriptionText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.9),
|
||||
);
|
||||
|
||||
static TextStyle detailsTitleText(CupertinoThemeData themeData) => themeData
|
||||
.textTheme
|
||||
.textStyle
|
||||
.copyWith(fontSize: 30, fontWeight: FontWeight.bold);
|
||||
|
||||
static TextStyle detailsPreferredCategoryText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(fontWeight: FontWeight.bold);
|
||||
|
||||
static TextStyle detailsBoldDescriptionText(CupertinoThemeData themeData) =>
|
||||
static TextStyle detailsTitleText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.9),
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
|
||||
static TextStyle detailsServingHeaderText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(
|
||||
color: const Color.fromRGBO(176, 176, 176, 1),
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
static TextStyle detailsPreferredCategoryText(
|
||||
CupertinoThemeData themeData,
|
||||
) => themeData.textTheme.textStyle.copyWith(fontWeight: FontWeight.bold);
|
||||
|
||||
static TextStyle detailsBoldDescriptionText(
|
||||
CupertinoThemeData themeData,
|
||||
) => themeData.textTheme.textStyle.copyWith(
|
||||
color: const Color.fromRGBO(0, 0, 0, 0.9),
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
|
||||
static TextStyle detailsServingHeaderText(
|
||||
CupertinoThemeData themeData,
|
||||
) => themeData.textTheme.textStyle.copyWith(
|
||||
color: const Color.fromRGBO(176, 176, 176, 1),
|
||||
fontWeight: FontWeight.bold,
|
||||
);
|
||||
|
||||
static TextStyle detailsServingLabelText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(fontWeight: FontWeight.bold);
|
||||
@@ -103,8 +107,8 @@ abstract class Styles {
|
||||
|
||||
static Color? scaffoldBackground(Brightness brightness) =>
|
||||
brightness == Brightness.light
|
||||
? CupertinoColors.extraLightBackgroundGray
|
||||
: null;
|
||||
? CupertinoColors.extraLightBackgroundGray
|
||||
: null;
|
||||
|
||||
static const frostedBackground = Color(0xccf8f8f8);
|
||||
|
||||
@@ -112,8 +116,12 @@ abstract class Styles {
|
||||
|
||||
static const closeButtonPressed = Color(0xff808080);
|
||||
|
||||
static TextStyle settingsItemSubtitleText(CupertinoThemeData themeData) =>
|
||||
themeData.textTheme.textStyle.copyWith(fontSize: 12, letterSpacing: -0.2);
|
||||
static TextStyle settingsItemSubtitleText(
|
||||
CupertinoThemeData themeData,
|
||||
) => themeData.textTheme.textStyle.copyWith(
|
||||
fontSize: 12,
|
||||
letterSpacing: -0.2,
|
||||
);
|
||||
|
||||
static const Color searchCursorColor = Color.fromRGBO(0, 122, 255, 1);
|
||||
|
||||
@@ -177,13 +185,13 @@ abstract class Styles {
|
||||
|
||||
static Color settingsItemColor(Brightness brightness) =>
|
||||
brightness == Brightness.light
|
||||
? CupertinoColors.tertiarySystemBackground
|
||||
: CupertinoColors.darkBackgroundGray;
|
||||
? CupertinoColors.tertiarySystemBackground
|
||||
: CupertinoColors.darkBackgroundGray;
|
||||
|
||||
static Color settingsLineation(Brightness brightness) =>
|
||||
brightness == Brightness.light
|
||||
? const Color(0xffbcbbc1)
|
||||
: const Color(0xff4c4b4b);
|
||||
? const Color(0xffbcbbc1)
|
||||
: const Color(0xff4c4b4b);
|
||||
|
||||
static const Color settingsBackground = Color(0xffefeff4);
|
||||
|
||||
@@ -208,6 +216,6 @@ abstract class Styles {
|
||||
static const servingInfoBorderColor = Color(0xffb0b0b0);
|
||||
|
||||
static const ColorFilter desaturatedColorFilter =
|
||||
// 222222 is a random color that has low color saturation.
|
||||
ColorFilter.mode(Color(0xff222222), BlendMode.saturation);
|
||||
// 222222 is a random color that has low color saturation.
|
||||
ColorFilter.mode(Color(0xff222222), BlendMode.saturation);
|
||||
}
|
||||
|
||||
@@ -86,11 +86,14 @@ class ShareButton extends _DetailPageButton {
|
||||
|
||||
/// A favorite button that invokes a callback when pressed.
|
||||
class FavoriteButton extends _DetailPageButton {
|
||||
const FavoriteButton(VoidCallback onPressed, bool isFavorite, {super.key})
|
||||
: super(
|
||||
onPressed,
|
||||
isFavorite ? CupertinoIcons.heart_fill : CupertinoIcons.heart,
|
||||
);
|
||||
const FavoriteButton(
|
||||
VoidCallback onPressed,
|
||||
bool isFavorite, {
|
||||
super.key,
|
||||
}) : super(
|
||||
onPressed,
|
||||
isFavorite ? CupertinoIcons.heart_fill : CupertinoIcons.heart,
|
||||
);
|
||||
}
|
||||
|
||||
class _DetailPageButton extends StatefulWidget {
|
||||
@@ -124,15 +127,16 @@ class _DetailPageButtonState extends State<_DetailPageButton> {
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(borderRadius: BorderRadius.circular(15)),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: Center(
|
||||
child: ColorChangingIcon(
|
||||
widget.icon,
|
||||
duration: const Duration(milliseconds: 300),
|
||||
color:
|
||||
tapInProgress
|
||||
? Styles.closeButtonPressed
|
||||
: Styles.closeButtonUnpressed,
|
||||
color: tapInProgress
|
||||
? Styles.closeButtonPressed
|
||||
: Styles.closeButtonUnpressed,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -93,8 +93,9 @@ class VeggieCard extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
colorFilter:
|
||||
isInSeason ? null : Styles.desaturatedColorFilter,
|
||||
colorFilter: isInSeason
|
||||
? null
|
||||
: Styles.desaturatedColorFilter,
|
||||
image: AssetImage(veggie.imageAssetPath),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -67,6 +67,9 @@ class VeggieHeadline extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final themeData = CupertinoTheme.of(context);
|
||||
final String location = GoRouter.of(
|
||||
context,
|
||||
).routerDelegate.currentConfiguration.uri.toString();
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
@@ -74,7 +77,7 @@ class VeggieHeadline extends StatelessWidget {
|
||||
// so navigate to the absolute route, which can be either
|
||||
// `/favorites/details/${veggie.id}` or `/search/details/${veggie.id}`
|
||||
// see https://github.com/flutter/flutter/issues/108177
|
||||
context.go('${GoRouter.of(context).location}/details/${veggie.id}');
|
||||
context.go('$location/details/${veggie.id}');
|
||||
},
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -93,7 +96,10 @@ class VeggieHeadline extends StatelessWidget {
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(veggie.name, style: Styles.headlineName(themeData)),
|
||||
Text(
|
||||
veggie.name,
|
||||
style: Styles.headlineName(themeData),
|
||||
),
|
||||
..._buildSeasonDots(veggie.seasons),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -19,7 +19,8 @@ class VeggieSeasonsPage<T> extends Page<T> {
|
||||
}
|
||||
|
||||
class VeggieSeasonsPageRoute<T> extends PageRoute<T> {
|
||||
VeggieSeasonsPageRoute(VeggieSeasonsPage<T> page) : super(settings: page);
|
||||
VeggieSeasonsPageRoute(VeggieSeasonsPage<T> page)
|
||||
: super(settings: page);
|
||||
|
||||
VeggieSeasonsPage<T> get _page => settings as VeggieSeasonsPage<T>;
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
name: veggieseasons
|
||||
description: An iOS app that shows the fruits and veggies currently in season.
|
||||
publish_to: none
|
||||
|
||||
version: 1.2.0
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.0-0
|
||||
sdk: ^3.9.0-0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
cupertino_icons: ^1.0.2
|
||||
font_awesome_flutter: ^10.1.0
|
||||
intl: ^0.20.0
|
||||
@@ -18,11 +17,11 @@ dependencies:
|
||||
shared_preferences: ^2.0.14
|
||||
window_size:
|
||||
git:
|
||||
url: https://github.com/google/flutter-desktop-embedding
|
||||
url: https://github.com/google/flutter-desktop-embedding.git
|
||||
path: plugins/window_size
|
||||
# TODO: https://github.com/flutter/samples/issues/1838
|
||||
# go_router ^7.1.0 is breaking the state restoration tests
|
||||
go_router: 7.0.2
|
||||
go_router: ^16.0.0
|
||||
|
||||
dev_dependencies:
|
||||
analysis_defaults:
|
||||
|
||||
Reference in New Issue
Block a user