diff --git a/veggieseasons/.gitignore b/experimental/veggieseasons/.gitignore similarity index 100% rename from veggieseasons/.gitignore rename to experimental/veggieseasons/.gitignore diff --git a/veggieseasons/.metadata b/experimental/veggieseasons/.metadata similarity index 100% rename from veggieseasons/.metadata rename to experimental/veggieseasons/.metadata diff --git a/experimental/veggieseasons/README.md b/experimental/veggieseasons/README.md new file mode 100644 index 000000000..114b3563a --- /dev/null +++ b/experimental/veggieseasons/README.md @@ -0,0 +1,42 @@ +# Veggie Seasons + +An iOS app that shows which fruits and vegetables are currently in season. It +showcases Flutter's Cupertino package. + +[Available now in the App Store!](https://itunes.apple.com/is/app/veggie-seasons/id1450855435) + +**NOTE:** While Flutter supports many platforms, this application is designed +specifically for iOS. It's not intended to be run on Android, web, or desktop. + +## Goals + +* Show how to build an interface that iOS users will feel right at home + with. +* Show how Flutter's Cupertino widgets work together. + +## The important bits + +### `/screens/*` + +These are the screens presented in the app, roughly analogous to +UIViewControllers. `HomeScreen` is the root, and the others are shown +as the user navigates. + +### `/widgets/search_bar.dart` + +An example of how to construct an Cupertino-style search bar. The +Flutter team [is working on an official widget](https://github.com/flutter/flutter/issues/9784) +for this. Once that effort is complete, developers will not need to roll +their own search bars, so to speak. + +## Questions/issues + +If you have a general question about any of the techniques you see in +the sample, the best places to go are: + +* [The FlutterDev Google Group](https://groups.google.com/forum/#!forum/flutter-dev) +* [The Flutter Gitter channel](https://gitter.im/flutter/flutter) +* [StackOverflow](https://stackoverflow.com/questions/tagged/flutter) + +If you run into an issue with the sample itself, please file an issue +in the [main Flutter repo](https://github.com/flutter/flutter/issues). diff --git a/veggieseasons/analysis_options.yaml b/experimental/veggieseasons/analysis_options.yaml similarity index 100% rename from veggieseasons/analysis_options.yaml rename to experimental/veggieseasons/analysis_options.yaml diff --git a/veggieseasons/assets/fonts/NotoSans-Bold.ttf b/experimental/veggieseasons/assets/fonts/NotoSans-Bold.ttf similarity index 100% rename from veggieseasons/assets/fonts/NotoSans-Bold.ttf rename to experimental/veggieseasons/assets/fonts/NotoSans-Bold.ttf diff --git a/veggieseasons/assets/fonts/NotoSans-BoldItalic.ttf b/experimental/veggieseasons/assets/fonts/NotoSans-BoldItalic.ttf similarity index 100% rename from veggieseasons/assets/fonts/NotoSans-BoldItalic.ttf rename to experimental/veggieseasons/assets/fonts/NotoSans-BoldItalic.ttf diff --git a/veggieseasons/assets/fonts/NotoSans-Italic.ttf b/experimental/veggieseasons/assets/fonts/NotoSans-Italic.ttf similarity index 100% rename from veggieseasons/assets/fonts/NotoSans-Italic.ttf rename to experimental/veggieseasons/assets/fonts/NotoSans-Italic.ttf diff --git a/veggieseasons/assets/fonts/NotoSans-Regular.ttf b/experimental/veggieseasons/assets/fonts/NotoSans-Regular.ttf similarity index 100% rename from veggieseasons/assets/fonts/NotoSans-Regular.ttf rename to experimental/veggieseasons/assets/fonts/NotoSans-Regular.ttf diff --git a/veggieseasons/assets/icon/launcher_icon.png b/experimental/veggieseasons/assets/icon/launcher_icon.png similarity index 100% rename from veggieseasons/assets/icon/launcher_icon.png rename to experimental/veggieseasons/assets/icon/launcher_icon.png diff --git a/veggieseasons/assets/images/apple.jpg b/experimental/veggieseasons/assets/images/apple.jpg similarity index 100% rename from veggieseasons/assets/images/apple.jpg rename to experimental/veggieseasons/assets/images/apple.jpg diff --git a/veggieseasons/assets/images/artichoke.jpg b/experimental/veggieseasons/assets/images/artichoke.jpg similarity index 100% rename from veggieseasons/assets/images/artichoke.jpg rename to experimental/veggieseasons/assets/images/artichoke.jpg diff --git a/veggieseasons/assets/images/asparagus.jpg b/experimental/veggieseasons/assets/images/asparagus.jpg similarity index 100% rename from veggieseasons/assets/images/asparagus.jpg rename to experimental/veggieseasons/assets/images/asparagus.jpg diff --git a/veggieseasons/assets/images/avocado.jpg b/experimental/veggieseasons/assets/images/avocado.jpg similarity index 100% rename from veggieseasons/assets/images/avocado.jpg rename to experimental/veggieseasons/assets/images/avocado.jpg diff --git a/veggieseasons/assets/images/blackberry.jpg b/experimental/veggieseasons/assets/images/blackberry.jpg similarity index 100% rename from veggieseasons/assets/images/blackberry.jpg rename to experimental/veggieseasons/assets/images/blackberry.jpg diff --git a/veggieseasons/assets/images/cantaloupe.jpg b/experimental/veggieseasons/assets/images/cantaloupe.jpg similarity index 100% rename from veggieseasons/assets/images/cantaloupe.jpg rename to experimental/veggieseasons/assets/images/cantaloupe.jpg diff --git a/veggieseasons/assets/images/cauliflower.jpg b/experimental/veggieseasons/assets/images/cauliflower.jpg similarity index 100% rename from veggieseasons/assets/images/cauliflower.jpg rename to experimental/veggieseasons/assets/images/cauliflower.jpg diff --git a/veggieseasons/assets/images/endive.jpg b/experimental/veggieseasons/assets/images/endive.jpg similarity index 100% rename from veggieseasons/assets/images/endive.jpg rename to experimental/veggieseasons/assets/images/endive.jpg diff --git a/veggieseasons/assets/images/fig.jpg b/experimental/veggieseasons/assets/images/fig.jpg similarity index 100% rename from veggieseasons/assets/images/fig.jpg rename to experimental/veggieseasons/assets/images/fig.jpg diff --git a/veggieseasons/assets/images/grape.jpg b/experimental/veggieseasons/assets/images/grape.jpg similarity index 100% rename from veggieseasons/assets/images/grape.jpg rename to experimental/veggieseasons/assets/images/grape.jpg diff --git a/veggieseasons/assets/images/green_bell_pepper.jpg b/experimental/veggieseasons/assets/images/green_bell_pepper.jpg similarity index 100% rename from veggieseasons/assets/images/green_bell_pepper.jpg rename to experimental/veggieseasons/assets/images/green_bell_pepper.jpg diff --git a/veggieseasons/assets/images/habanero.jpg b/experimental/veggieseasons/assets/images/habanero.jpg similarity index 100% rename from veggieseasons/assets/images/habanero.jpg rename to experimental/veggieseasons/assets/images/habanero.jpg diff --git a/veggieseasons/assets/images/kale.jpg b/experimental/veggieseasons/assets/images/kale.jpg similarity index 100% rename from veggieseasons/assets/images/kale.jpg rename to experimental/veggieseasons/assets/images/kale.jpg diff --git a/veggieseasons/assets/images/kiwi.jpg b/experimental/veggieseasons/assets/images/kiwi.jpg similarity index 100% rename from veggieseasons/assets/images/kiwi.jpg rename to experimental/veggieseasons/assets/images/kiwi.jpg diff --git a/veggieseasons/assets/images/lemon.jpg b/experimental/veggieseasons/assets/images/lemon.jpg similarity index 100% rename from veggieseasons/assets/images/lemon.jpg rename to experimental/veggieseasons/assets/images/lemon.jpg diff --git a/veggieseasons/assets/images/lime.jpg b/experimental/veggieseasons/assets/images/lime.jpg similarity index 100% rename from veggieseasons/assets/images/lime.jpg rename to experimental/veggieseasons/assets/images/lime.jpg diff --git a/veggieseasons/assets/images/mango.jpg b/experimental/veggieseasons/assets/images/mango.jpg similarity index 100% rename from veggieseasons/assets/images/mango.jpg rename to experimental/veggieseasons/assets/images/mango.jpg diff --git a/veggieseasons/assets/images/mushroom.jpg b/experimental/veggieseasons/assets/images/mushroom.jpg similarity index 100% rename from veggieseasons/assets/images/mushroom.jpg rename to experimental/veggieseasons/assets/images/mushroom.jpg diff --git a/veggieseasons/assets/images/nectarine.jpg b/experimental/veggieseasons/assets/images/nectarine.jpg similarity index 100% rename from veggieseasons/assets/images/nectarine.jpg rename to experimental/veggieseasons/assets/images/nectarine.jpg diff --git a/veggieseasons/assets/images/orange_bell_pepper.jpg b/experimental/veggieseasons/assets/images/orange_bell_pepper.jpg similarity index 100% rename from veggieseasons/assets/images/orange_bell_pepper.jpg rename to experimental/veggieseasons/assets/images/orange_bell_pepper.jpg diff --git a/veggieseasons/assets/images/persimmon.jpg b/experimental/veggieseasons/assets/images/persimmon.jpg similarity index 100% rename from veggieseasons/assets/images/persimmon.jpg rename to experimental/veggieseasons/assets/images/persimmon.jpg diff --git a/veggieseasons/assets/images/plum.jpg b/experimental/veggieseasons/assets/images/plum.jpg similarity index 100% rename from veggieseasons/assets/images/plum.jpg rename to experimental/veggieseasons/assets/images/plum.jpg diff --git a/veggieseasons/assets/images/potato.jpg b/experimental/veggieseasons/assets/images/potato.jpg similarity index 100% rename from veggieseasons/assets/images/potato.jpg rename to experimental/veggieseasons/assets/images/potato.jpg diff --git a/veggieseasons/assets/images/radicchio.jpg b/experimental/veggieseasons/assets/images/radicchio.jpg similarity index 100% rename from veggieseasons/assets/images/radicchio.jpg rename to experimental/veggieseasons/assets/images/radicchio.jpg diff --git a/veggieseasons/assets/images/radish.jpg b/experimental/veggieseasons/assets/images/radish.jpg similarity index 100% rename from veggieseasons/assets/images/radish.jpg rename to experimental/veggieseasons/assets/images/radish.jpg diff --git a/veggieseasons/assets/images/squash.jpg b/experimental/veggieseasons/assets/images/squash.jpg similarity index 100% rename from veggieseasons/assets/images/squash.jpg rename to experimental/veggieseasons/assets/images/squash.jpg diff --git a/veggieseasons/assets/images/strawberry.jpg b/experimental/veggieseasons/assets/images/strawberry.jpg similarity index 100% rename from veggieseasons/assets/images/strawberry.jpg rename to experimental/veggieseasons/assets/images/strawberry.jpg diff --git a/veggieseasons/assets/images/tangelo.jpg b/experimental/veggieseasons/assets/images/tangelo.jpg similarity index 100% rename from veggieseasons/assets/images/tangelo.jpg rename to experimental/veggieseasons/assets/images/tangelo.jpg diff --git a/veggieseasons/assets/images/tomato.jpg b/experimental/veggieseasons/assets/images/tomato.jpg similarity index 100% rename from veggieseasons/assets/images/tomato.jpg rename to experimental/veggieseasons/assets/images/tomato.jpg diff --git a/veggieseasons/assets/images/watermelon.jpg b/experimental/veggieseasons/assets/images/watermelon.jpg similarity index 100% rename from veggieseasons/assets/images/watermelon.jpg rename to experimental/veggieseasons/assets/images/watermelon.jpg diff --git a/veggieseasons/ios/.gitignore b/experimental/veggieseasons/ios/.gitignore similarity index 100% rename from veggieseasons/ios/.gitignore rename to experimental/veggieseasons/ios/.gitignore diff --git a/veggieseasons/ios/Flutter/AppFrameworkInfo.plist b/experimental/veggieseasons/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from veggieseasons/ios/Flutter/AppFrameworkInfo.plist rename to experimental/veggieseasons/ios/Flutter/AppFrameworkInfo.plist diff --git a/veggieseasons/ios/Flutter/Debug.xcconfig b/experimental/veggieseasons/ios/Flutter/Debug.xcconfig similarity index 100% rename from veggieseasons/ios/Flutter/Debug.xcconfig rename to experimental/veggieseasons/ios/Flutter/Debug.xcconfig diff --git a/veggieseasons/ios/Flutter/Release.xcconfig b/experimental/veggieseasons/ios/Flutter/Release.xcconfig similarity index 100% rename from veggieseasons/ios/Flutter/Release.xcconfig rename to experimental/veggieseasons/ios/Flutter/Release.xcconfig diff --git a/veggieseasons/ios/Podfile b/experimental/veggieseasons/ios/Podfile similarity index 100% rename from veggieseasons/ios/Podfile rename to experimental/veggieseasons/ios/Podfile diff --git a/veggieseasons/ios/Podfile.lock b/experimental/veggieseasons/ios/Podfile.lock similarity index 100% rename from veggieseasons/ios/Podfile.lock rename to experimental/veggieseasons/ios/Podfile.lock diff --git a/veggieseasons/ios/Runner.xcodeproj/project.pbxproj b/experimental/veggieseasons/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from veggieseasons/ios/Runner.xcodeproj/project.pbxproj rename to experimental/veggieseasons/ios/Runner.xcodeproj/project.pbxproj diff --git a/veggieseasons/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/experimental/veggieseasons/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from veggieseasons/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to experimental/veggieseasons/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/veggieseasons/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/experimental/veggieseasons/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from veggieseasons/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to experimental/veggieseasons/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/veggieseasons/ios/Runner.xcworkspace/contents.xcworkspacedata b/experimental/veggieseasons/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from veggieseasons/ios/Runner.xcworkspace/contents.xcworkspacedata rename to experimental/veggieseasons/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/veggieseasons/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/experimental/veggieseasons/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from veggieseasons/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to experimental/veggieseasons/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/veggieseasons/ios/Runner/AppDelegate.swift b/experimental/veggieseasons/ios/Runner/AppDelegate.swift similarity index 100% rename from veggieseasons/ios/Runner/AppDelegate.swift rename to experimental/veggieseasons/ios/Runner/AppDelegate.swift diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to experimental/veggieseasons/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/veggieseasons/ios/Runner/Base.lproj/LaunchScreen.storyboard b/experimental/veggieseasons/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from veggieseasons/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to experimental/veggieseasons/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/veggieseasons/ios/Runner/Base.lproj/Main.storyboard b/experimental/veggieseasons/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from veggieseasons/ios/Runner/Base.lproj/Main.storyboard rename to experimental/veggieseasons/ios/Runner/Base.lproj/Main.storyboard diff --git a/veggieseasons/ios/Runner/Info.plist b/experimental/veggieseasons/ios/Runner/Info.plist similarity index 100% rename from veggieseasons/ios/Runner/Info.plist rename to experimental/veggieseasons/ios/Runner/Info.plist diff --git a/veggieseasons/ios/Runner/Runner-Bridging-Header.h b/experimental/veggieseasons/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from veggieseasons/ios/Runner/Runner-Bridging-Header.h rename to experimental/veggieseasons/ios/Runner/Runner-Bridging-Header.h diff --git a/veggieseasons/lib/data/app_state.dart b/experimental/veggieseasons/lib/data/app_state.dart similarity index 92% rename from veggieseasons/lib/data/app_state.dart rename to experimental/veggieseasons/lib/data/app_state.dart index 53d0f7806..193a2911c 100644 --- a/veggieseasons/lib/data/app_state.dart +++ b/experimental/veggieseasons/lib/data/app_state.dart @@ -38,7 +38,14 @@ class AppState extends ChangeNotifier { notifyListeners(); } + /// Used in tests to set the season independent of the current date. + static Season debugCurrentSeason; + static Season _getSeasonForDate(DateTime date) { + if (debugCurrentSeason != null) { + return debugCurrentSeason; + } + // Technically the start and end dates of seasons can vary by a day or so, // but this is close enough for produce. switch (date.month) { diff --git a/veggieseasons/lib/data/local_veggie_provider.dart b/experimental/veggieseasons/lib/data/local_veggie_provider.dart similarity index 100% rename from veggieseasons/lib/data/local_veggie_provider.dart rename to experimental/veggieseasons/lib/data/local_veggie_provider.dart diff --git a/veggieseasons/lib/data/preferences.dart b/experimental/veggieseasons/lib/data/preferences.dart similarity index 100% rename from veggieseasons/lib/data/preferences.dart rename to experimental/veggieseasons/lib/data/preferences.dart diff --git a/veggieseasons/lib/data/veggie.dart b/experimental/veggieseasons/lib/data/veggie.dart similarity index 100% rename from veggieseasons/lib/data/veggie.dart rename to experimental/veggieseasons/lib/data/veggie.dart diff --git a/experimental/veggieseasons/lib/main.dart b/experimental/veggieseasons/lib/main.dart new file mode 100644 index 000000000..2792b1fd0 --- /dev/null +++ b/experimental/veggieseasons/lib/main.dart @@ -0,0 +1,89 @@ +// 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 'package:flutter/services.dart' show DeviceOrientation, SystemChrome; +import 'package:provider/provider.dart'; +import 'package:veggieseasons/data/app_state.dart'; +import 'package:veggieseasons/data/preferences.dart'; +import 'package:veggieseasons/screens/home.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + + runApp( + RootRestorationScope( + restorationId: 'root', + child: VeggieApp(), + ), + ); +} + +class VeggieApp extends StatefulWidget { + @override + State createState() => _VeggieAppState(); +} + +class _VeggieAppState extends State with RestorationMixin { + final _RestorableAppState _appState = _RestorableAppState(); + + @override + String get restorationId => 'wrapper'; + + @override + void restoreState(RestorationBucket oldBucket, bool initialRestore) { + registerForRestoration(_appState, 'state'); + } + + @override + void dispose() { + _appState.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider.value( + value: _appState.value, + ), + ChangeNotifierProvider( + create: (_) => Preferences()..load(), + ), + ], + child: CupertinoApp( + debugShowCheckedModeBanner: false, + home: HomeScreen(restorationId: 'home'), + restorationScopeId: 'app', + ), + ); + } +} + +class _RestorableAppState extends RestorableListenable { + @override + AppState createDefaultValue() { + return AppState(); + } + + @override + AppState fromPrimitives(Object data) { + final appState = AppState(); + final favorites = (data as List).cast(); + for (var id in favorites) { + appState.setFavorite(id, true); + } + return appState; + } + + @override + Object toPrimitives() { + return value.favoriteVeggies.map((veggie) => veggie.id).toList(); + } +} diff --git a/veggieseasons/lib/screens/details.dart b/experimental/veggieseasons/lib/screens/details.dart similarity index 81% rename from veggieseasons/lib/screens/details.dart rename to experimental/veggieseasons/lib/screens/details.dart index 1bcca04b7..bdd6e3f94 100644 --- a/veggieseasons/lib/screens/details.dart +++ b/experimental/veggieseasons/lib/screens/details.dart @@ -238,15 +238,42 @@ class InfoView extends StatelessWidget { class DetailsScreen extends StatefulWidget { final int id; + final String restorationId; - DetailsScreen(this.id); + DetailsScreen({this.id, this.restorationId}); + + static String show(NavigatorState navigator, int veggieId) { + return navigator.restorablePush(_routeBuilder, arguments: veggieId); + } + + static Route _routeBuilder(BuildContext context, Object arguments) { + final veggieId = arguments as int; + return CupertinoPageRoute( + builder: (context) => DetailsScreen(id: veggieId, restorationId: 'details'), + fullscreenDialog: true, + ); + } @override _DetailsScreenState createState() => _DetailsScreenState(); } -class _DetailsScreenState extends State { - int _selectedViewIndex = 0; +class _DetailsScreenState extends State with RestorationMixin { + final RestorableInt _selectedViewIndex = RestorableInt(0); + + @override + String get restorationId => widget.restorationId; + + @override + void restoreState(RestorationBucket oldBucket, bool initialRestore) { + registerForRestoration(_selectedViewIndex, 'tab'); + } + + @override + void dispose() { + _selectedViewIndex.dispose(); + super.dispose(); + } Widget _buildHeader(BuildContext context, AppState model) { final veggie = model.getVeggie(widget.id); @@ -282,33 +309,37 @@ class _DetailsScreenState extends State { Widget build(BuildContext context) { final appState = Provider.of(context); - return CupertinoPageScaffold( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: ListView( - children: [ - _buildHeader(context, appState), - SizedBox(height: 20), - CupertinoSegmentedControl( - children: { - 0: Text('Facts & Info'), - 1: Text('Trivia'), - }, - groupValue: _selectedViewIndex, - onValueChanged: (value) { - setState(() => _selectedViewIndex = value); - }, - ), - _selectedViewIndex == 0 - ? InfoView(widget.id) - : TriviaView(widget.id), - ], + return UnmanagedRestorationScope( + bucket: bucket, + child: CupertinoPageScaffold( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: ListView( + restorationId: 'list', + children: [ + _buildHeader(context, appState), + SizedBox(height: 20), + CupertinoSegmentedControl( + children: { + 0: Text('Facts & Info'), + 1: Text('Trivia'), + }, + groupValue: _selectedViewIndex.value, + onValueChanged: (value) { + setState(() => _selectedViewIndex.value = value); + }, + ), + _selectedViewIndex.value == 0 + ? InfoView(widget.id) + : TriviaView(id: widget.id, restorationId: 'trivia'), + ], + ), ), - ), - ], + ], + ), ), ); } diff --git a/veggieseasons/lib/screens/favorites.dart b/experimental/veggieseasons/lib/screens/favorites.dart similarity index 90% rename from veggieseasons/lib/screens/favorites.dart rename to experimental/veggieseasons/lib/screens/favorites.dart index f565c6be9..05446673e 100644 --- a/veggieseasons/lib/screens/favorites.dart +++ b/experimental/veggieseasons/lib/screens/favorites.dart @@ -11,9 +11,14 @@ import 'package:veggieseasons/styles.dart'; import 'package:veggieseasons/widgets/veggie_headline.dart'; class FavoritesScreen extends StatelessWidget { + FavoritesScreen({this.restorationId, Key key}) : super(key: key); + + final String restorationId; + @override Widget build(BuildContext context) { return CupertinoTabView( + restorationScopeId: restorationId, builder: (context) { final model = Provider.of(context); @@ -32,6 +37,7 @@ class FavoritesScreen extends StatelessWidget { ), ) : ListView( + restorationId: 'list', children: [ SizedBox(height: 24), for (Veggie veggie in model.favoriteVeggies) diff --git a/experimental/veggieseasons/lib/screens/home.dart b/experimental/veggieseasons/lib/screens/home.dart new file mode 100644 index 000000000..45fb96bd0 --- /dev/null +++ b/experimental/veggieseasons/lib/screens/home.dart @@ -0,0 +1,55 @@ +// 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 'package:flutter/widgets.dart'; +import 'package:veggieseasons/screens/favorites.dart'; +import 'package:veggieseasons/screens/list.dart'; +import 'package:veggieseasons/screens/search.dart'; +import 'package:veggieseasons/screens/settings.dart'; + +class HomeScreen extends StatelessWidget { + HomeScreen({Key key, this.restorationId}) : super(key: key); + + final String restorationId; + + @override + Widget build(BuildContext context) { + return RestorationScope( + restorationId: restorationId, + child: CupertinoTabScaffold( + restorationId: 'scaffold', + tabBar: CupertinoTabBar(items: [ + BottomNavigationBarItem( + icon: Icon(CupertinoIcons.home), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Icon(CupertinoIcons.book), + label: 'My Garden', + ), + BottomNavigationBarItem( + icon: Icon(CupertinoIcons.search), + label: 'Search', + ), + BottomNavigationBarItem( + icon: Icon(CupertinoIcons.settings), + label: 'Settings', + ), + ]), + tabBuilder: (context, index) { + if (index == 0) { + return ListScreen(restorationId: 'list'); + } else if (index == 1) { + return FavoritesScreen(restorationId: 'favorites'); + } else if (index == 2) { + return SearchScreen(restorationId: 'search'); + } else { + return SettingsScreen(restorationId: 'settings'); + } + }, + ), + ); + } +} diff --git a/veggieseasons/lib/screens/list.dart b/experimental/veggieseasons/lib/screens/list.dart similarity index 94% rename from veggieseasons/lib/screens/list.dart rename to experimental/veggieseasons/lib/screens/list.dart index 288ee1758..8b49ce941 100644 --- a/veggieseasons/lib/screens/list.dart +++ b/experimental/veggieseasons/lib/screens/list.dart @@ -13,6 +13,10 @@ import 'package:veggieseasons/styles.dart'; import 'package:veggieseasons/widgets/veggie_card.dart'; class ListScreen extends StatelessWidget { + ListScreen({this.restorationId, Key key}) : super(key: key); + + final String restorationId; + Widget _generateVeggieRow(Veggie veggie, Preferences prefs, {bool inSeason = true}) { return Padding( @@ -29,6 +33,7 @@ class ListScreen extends StatelessWidget { @override Widget build(BuildContext context) { return CupertinoTabView( + restorationScopeId: restorationId, builder: (context) { var dateString = DateFormat('MMMM y').format(DateTime.now()); @@ -38,6 +43,7 @@ class ListScreen extends StatelessWidget { return SafeArea( bottom: false, child: ListView.builder( + restorationId: 'list', itemCount: appState.allVeggies.length + 2, itemBuilder: (context, index) { if (index == 0) { diff --git a/veggieseasons/lib/screens/search.dart b/experimental/veggieseasons/lib/screens/search.dart similarity index 70% rename from veggieseasons/lib/screens/search.dart rename to experimental/veggieseasons/lib/screens/search.dart index 43e3bb234..21c5b242f 100644 --- a/veggieseasons/lib/screens/search.dart +++ b/experimental/veggieseasons/lib/screens/search.dart @@ -12,36 +12,45 @@ import 'package:veggieseasons/widgets/search_bar.dart'; import 'package:veggieseasons/widgets/veggie_headline.dart'; class SearchScreen extends StatefulWidget { + SearchScreen({this.restorationId, Key key}) : super(key: key); + + final String restorationId; + @override _SearchScreenState createState() => _SearchScreenState(); } -class _SearchScreenState extends State { - final controller = TextEditingController(); +class _SearchScreenState extends State with RestorationMixin { + final controller = RestorableTextEditingController(); final focusNode = FocusNode(); - String terms = ''; + String terms; @override - void initState() { - super.initState(); + String get restorationId => widget.restorationId; + + @override + void restoreState(RestorationBucket oldBucket, bool initialRestore) { + registerForRestoration(controller, 'text'); controller.addListener(_onTextChanged); + terms = controller.value.text; } @override void dispose() { focusNode.dispose(); + controller.dispose(); super.dispose(); } void _onTextChanged() { - setState(() => terms = controller.text); + setState(() => terms = controller.value.text); } Widget _createSearchBox() { return Padding( padding: const EdgeInsets.all(8), child: SearchBar( - controller: controller, + controller: controller.value, focusNode: focusNode, ), ); @@ -61,6 +70,7 @@ class _SearchScreenState extends State { } return ListView.builder( + restorationId: 'list', itemCount: veggies.length + 1, itemBuilder: (context, i) { if (i == 0) { @@ -88,18 +98,20 @@ class _SearchScreenState extends State { Widget build(BuildContext context) { final model = Provider.of(context); - return CupertinoTabView( - builder: (context) { - return SafeArea( - bottom: false, - child: Stack( - children: [ - _buildSearchResults(model.searchVeggies(terms)), - _createSearchBox(), - ], - ), - ); - }, + return UnmanagedRestorationScope( + child: CupertinoTabView( + builder: (context) { + return SafeArea( + bottom: false, + child: Stack( + children: [ + _buildSearchResults(model.searchVeggies(terms)), + _createSearchBox(), + ], + ), + ); + }, + ), ); } } diff --git a/experimental/veggieseasons/lib/screens/settings.dart b/experimental/veggieseasons/lib/screens/settings.dart new file mode 100644 index 000000000..6accd80d6 --- /dev/null +++ b/experimental/veggieseasons/lib/screens/settings.dart @@ -0,0 +1,250 @@ +// 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 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:provider/provider.dart'; +import 'package:veggieseasons/data/preferences.dart'; +import 'package:veggieseasons/data/veggie.dart'; +import 'package:veggieseasons/styles.dart'; +import 'package:veggieseasons/widgets/settings_group.dart'; +import 'package:veggieseasons/widgets/settings_item.dart'; + +class VeggieCategorySettingsScreen extends StatelessWidget { + VeggieCategorySettingsScreen({Key key, this.restorationId}) : super(key: key); + + final String restorationId; + + static String show(NavigatorState navigator) { + return navigator.restorablePush(_routeBuilder); + } + + static Route _routeBuilder(BuildContext context, Object argument) { + return CupertinoPageRoute( + builder: (context) => VeggieCategorySettingsScreen(restorationId: 'category'), + title: 'Preferred Categories', + ); + } + + @override + Widget build(BuildContext context) { + final model = Provider.of(context); + final currentPrefs = model.preferredCategories; + var brightness = CupertinoTheme.brightnessOf(context); + return RestorationScope( + restorationId: restorationId, + child: CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + middle: Text('Preferred Categories'), + previousPageTitle: 'Settings', + ), + backgroundColor: Styles.scaffoldBackground(brightness), + child: FutureBuilder>( + future: currentPrefs, + builder: (context, snapshot) { + final items = []; + + for (final category in VeggieCategory.values) { + CupertinoSwitch toggle; + + // It's possible that category data hasn't loaded from shared prefs + // yet, so display it if possible and fall back to disabled switches + // otherwise. + if (snapshot.hasData) { + toggle = CupertinoSwitch( + value: snapshot.data.contains(category), + onChanged: (value) { + if (value) { + model.addPreferredCategory(category); + } else { + model.removePreferredCategory(category); + } + }, + ); + } else { + toggle = CupertinoSwitch( + value: false, + onChanged: null, + ); + } + + items.add(SettingsItem( + label: veggieCategoryNames[category], + content: toggle, + )); + } + + return ListView( + restorationId: 'list', + children: [ + SettingsGroup( + items: items, + ), + ], + ); + }, + ), + ), + ); + } +} + +class CalorieSettingsScreen extends StatelessWidget { + CalorieSettingsScreen({Key key, this.restorationId}) : super(key: key); + + final String restorationId; + + static const max = 1000; + static const min = 2600; + static const step = 200; + + static String show(NavigatorState navigator) { + return navigator.restorablePush(_routeBuilder); + } + + static Route _routeBuilder(BuildContext context, Object argument) { + return CupertinoPageRoute( + builder: (context) => CalorieSettingsScreen(restorationId: 'calorie'), + title: 'Calorie Target', + ); + } + + @override + Widget build(BuildContext context) { + final model = Provider.of(context); + var brightness = CupertinoTheme.brightnessOf(context); + return RestorationScope( + restorationId: restorationId, + child: CupertinoPageScaffold( + navigationBar: CupertinoNavigationBar( + previousPageTitle: 'Settings', + ), + backgroundColor: Styles.scaffoldBackground(brightness), + child: ListView( + restorationId: 'list', + children: [ + FutureBuilder( + future: model.desiredCalories, + builder: (context, snapshot) { + final steps = []; + + for (var cals = max; cals < min; cals += step) { + steps.add( + SettingsItem( + label: cals.toString(), + icon: SettingsIcon( + icon: Styles.checkIcon, + foregroundColor: snapshot.hasData && snapshot.data == cals + ? CupertinoColors.activeBlue + : Styles.transparentColor, + backgroundColor: Styles.transparentColor, + ), + onPress: snapshot.hasData + ? () => model.setDesiredCalories(cals) + : null, + ), + ); + } + + return SettingsGroup( + items: steps, + header: SettingsGroupHeader('Available calorie levels'), + footer: SettingsGroupFooter('These are used for serving ' + 'calculations'), + ); + }, + ), + ], + ), + ), + ); + } +} + +class SettingsScreen extends StatelessWidget { + SettingsScreen({this.restorationId, Key key}) : super(key: key); + + final String restorationId; + + SettingsItem _buildCaloriesItem(BuildContext context, Preferences prefs) { + return SettingsItem( + label: 'Calorie Target', + icon: SettingsIcon( + backgroundColor: Styles.iconBlue, + icon: Styles.calorieIcon, + ), + content: FutureBuilder( + future: prefs.desiredCalories, + builder: (context, snapshot) { + return Row( + children: [ + Text( + snapshot.data?.toString() ?? '', + style: CupertinoTheme.of(context).textTheme.textStyle, + ), + SizedBox(width: 8), + SettingsNavigationIndicator(), + ], + ); + }, + ), + onPress: () { + CalorieSettingsScreen.show(Navigator.of(context)); + }, + ); + } + + SettingsItem _buildCategoriesItem(BuildContext context, Preferences prefs) { + return SettingsItem( + label: 'Preferred Categories', + subtitle: 'What types of veggies you prefer!', + icon: SettingsIcon( + backgroundColor: Styles.iconGold, + icon: Styles.preferenceIcon, + ), + content: SettingsNavigationIndicator(), + onPress: () { + VeggieCategorySettingsScreen.show(Navigator.of(context)); + }, + ); + } + + @override + Widget build(BuildContext context) { + final prefs = Provider.of(context); + + return RestorationScope( + restorationId: restorationId, + child: CupertinoPageScaffold( + child: Container( + color: Styles.scaffoldBackground(CupertinoTheme.brightnessOf(context)), + child: CustomScrollView( + restorationId: 'list', + slivers: [ + CupertinoSliverNavigationBar( + largeTitle: Text('Settings'), + ), + SliverSafeArea( + top: false, + sliver: SliverList( + delegate: SliverChildListDelegate( + [ + SettingsGroup( + items: [ + _buildCaloriesItem(context, prefs), + _buildCategoriesItem(context, prefs), + ], + ), + ], + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/veggieseasons/lib/styles.dart b/experimental/veggieseasons/lib/styles.dart similarity index 100% rename from veggieseasons/lib/styles.dart rename to experimental/veggieseasons/lib/styles.dart diff --git a/veggieseasons/lib/widgets/close_button.dart b/experimental/veggieseasons/lib/widgets/close_button.dart similarity index 100% rename from veggieseasons/lib/widgets/close_button.dart rename to experimental/veggieseasons/lib/widgets/close_button.dart diff --git a/veggieseasons/lib/widgets/search_bar.dart b/experimental/veggieseasons/lib/widgets/search_bar.dart similarity index 100% rename from veggieseasons/lib/widgets/search_bar.dart rename to experimental/veggieseasons/lib/widgets/search_bar.dart diff --git a/veggieseasons/lib/widgets/settings_group.dart b/experimental/veggieseasons/lib/widgets/settings_group.dart similarity index 100% rename from veggieseasons/lib/widgets/settings_group.dart rename to experimental/veggieseasons/lib/widgets/settings_group.dart diff --git a/veggieseasons/lib/widgets/settings_item.dart b/experimental/veggieseasons/lib/widgets/settings_item.dart similarity index 100% rename from veggieseasons/lib/widgets/settings_item.dart rename to experimental/veggieseasons/lib/widgets/settings_item.dart diff --git a/veggieseasons/lib/widgets/trivia.dart b/experimental/veggieseasons/lib/widgets/trivia.dart similarity index 74% rename from veggieseasons/lib/widgets/trivia.dart rename to experimental/veggieseasons/lib/widgets/trivia.dart index 11dcde679..a7a49b274 100644 --- a/veggieseasons/lib/widgets/trivia.dart +++ b/experimental/veggieseasons/lib/widgets/trivia.dart @@ -9,8 +9,9 @@ import 'package:veggieseasons/styles.dart'; /// the user's score. class TriviaView extends StatefulWidget { final int id; + final String restorationId; - const TriviaView(this.id); + const TriviaView({this.id, this.restorationId}); @override _TriviaViewState createState() => _TriviaViewState(); @@ -23,7 +24,7 @@ enum PlayerStatus { wasIncorrect, } -class _TriviaViewState extends State { +class _TriviaViewState extends State with RestorationMixin { /// Current app state. This is used to fetch veggie data. AppState appState; @@ -31,16 +32,26 @@ class _TriviaViewState extends State { Veggie veggie; /// Index of the current trivia question. - int triviaIndex = 0; + RestorableInt triviaIndex = RestorableInt(0); /// User's score on the current veggie. - int score = 0; + RestorableInt score = RestorableInt(0); /// Trivia question currently being displayed. - Trivia get currentTrivia => veggie.trivia[triviaIndex]; + Trivia get currentTrivia => veggie.trivia[triviaIndex.value]; /// The current state of the game. - PlayerStatus status = PlayerStatus.readyToAnswer; + _RestorablePlayerStatus status = _RestorablePlayerStatus(PlayerStatus.readyToAnswer); + + @override + String get restorationId => widget.restorationId; + + @override + void restoreState(RestorationBucket oldBucket, bool initialRestore) { + registerForRestoration(triviaIndex, 'index'); + registerForRestoration(score, 'score'); + registerForRestoration(status, 'status'); + } // Called at init and again if any dependencies (read: InheritedWidgets) on // on which this object relies are changed. @@ -72,11 +83,19 @@ class _TriviaViewState extends State { } } + @override + void dispose() { + triviaIndex.dispose(); + score.dispose(); + status.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { - if (triviaIndex >= veggie.trivia.length) { + if (triviaIndex.value >= veggie.trivia.length) { return _buildFinishedView(); - } else if (status == PlayerStatus.readyToAnswer) { + } else if (status.value == PlayerStatus.readyToAnswer) { return _buildQuestionView(); } else { return _buildResultView(); @@ -85,19 +104,19 @@ class _TriviaViewState extends State { void _resetGame() { setState(() { - triviaIndex = 0; - score = 0; - status = PlayerStatus.readyToAnswer; + triviaIndex.value = 0; + score.value = 0; + status.value = PlayerStatus.readyToAnswer; }); } void _processAnswer(int answerIndex) { setState(() { if (answerIndex == currentTrivia.correctAnswerIndex) { - status = PlayerStatus.wasCorrect; - score++; + status.value = PlayerStatus.wasCorrect; + score.value++; } else { - status = PlayerStatus.wasIncorrect; + status.value = PlayerStatus.wasIncorrect; } }); } @@ -193,7 +212,7 @@ class _TriviaViewState extends State { child: Column( children: [ Text( - status == PlayerStatus.wasCorrect + status.value == PlayerStatus.wasCorrect ? 'That\'s right!' : 'Sorry, that wasn\'t the right answer.', style: CupertinoTheme.of(context).textTheme.textStyle, @@ -202,8 +221,8 @@ class _TriviaViewState extends State { CupertinoButton( child: Text('Next Question'), onPressed: () => setState(() { - triviaIndex++; - status = PlayerStatus.readyToAnswer; + triviaIndex.value++; + status.value = PlayerStatus.readyToAnswer; }), ), ], @@ -211,3 +230,29 @@ class _TriviaViewState extends State { ); } } + +class _RestorablePlayerStatus extends RestorableValue { + _RestorablePlayerStatus(this._defaultValue); + + final PlayerStatus _defaultValue; + + @override + PlayerStatus createDefaultValue() { + return _defaultValue; + } + + @override + PlayerStatus fromPrimitives(Object data) { + return PlayerStatus.values[data as int]; + } + + @override + Object toPrimitives() { + return value.index; + } + + @override + void didUpdateValue(PlayerStatus oldValue) { + notifyListeners(); + } +} diff --git a/veggieseasons/lib/widgets/veggie_card.dart b/experimental/veggieseasons/lib/widgets/veggie_card.dart similarity index 95% rename from veggieseasons/lib/widgets/veggie_card.dart rename to experimental/veggieseasons/lib/widgets/veggie_card.dart index f04f69edc..c1d5e4956 100644 --- a/veggieseasons/lib/widgets/veggie_card.dart +++ b/experimental/veggieseasons/lib/widgets/veggie_card.dart @@ -142,12 +142,7 @@ class VeggieCard extends StatelessWidget { @override Widget build(BuildContext context) { return PressableCard( - onPressed: () { - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => DetailsScreen(veggie.id), - fullscreenDialog: true, - )); - }, + onPressed: () => DetailsScreen.show(Navigator.of(context), veggie.id), child: Stack( children: [ Semantics( diff --git a/veggieseasons/lib/widgets/veggie_headline.dart b/experimental/veggieseasons/lib/widgets/veggie_headline.dart similarity index 94% rename from veggieseasons/lib/widgets/veggie_headline.dart rename to experimental/veggieseasons/lib/widgets/veggie_headline.dart index 9d6bd93de..4b1ebb480 100644 --- a/veggieseasons/lib/widgets/veggie_headline.dart +++ b/experimental/veggieseasons/lib/widgets/veggie_headline.dart @@ -70,10 +70,7 @@ class VeggieHeadline extends StatelessWidget { final themeData = CupertinoTheme.of(context); return GestureDetector( - onTap: () => Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => DetailsScreen(veggie.id), - fullscreenDialog: true, - )), + onTap: () => DetailsScreen.show(Navigator.of(context), veggie.id), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/veggieseasons/macos/.gitignore b/experimental/veggieseasons/macos/.gitignore similarity index 100% rename from veggieseasons/macos/.gitignore rename to experimental/veggieseasons/macos/.gitignore diff --git a/veggieseasons/macos/Flutter/Flutter-Debug.xcconfig b/experimental/veggieseasons/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from veggieseasons/macos/Flutter/Flutter-Debug.xcconfig rename to experimental/veggieseasons/macos/Flutter/Flutter-Debug.xcconfig diff --git a/veggieseasons/macos/Flutter/Flutter-Release.xcconfig b/experimental/veggieseasons/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from veggieseasons/macos/Flutter/Flutter-Release.xcconfig rename to experimental/veggieseasons/macos/Flutter/Flutter-Release.xcconfig diff --git a/veggieseasons/macos/Flutter/GeneratedPluginRegistrant.swift b/experimental/veggieseasons/macos/Flutter/GeneratedPluginRegistrant.swift similarity index 100% rename from veggieseasons/macos/Flutter/GeneratedPluginRegistrant.swift rename to experimental/veggieseasons/macos/Flutter/GeneratedPluginRegistrant.swift diff --git a/veggieseasons/macos/Podfile b/experimental/veggieseasons/macos/Podfile similarity index 100% rename from veggieseasons/macos/Podfile rename to experimental/veggieseasons/macos/Podfile diff --git a/veggieseasons/macos/Podfile.lock b/experimental/veggieseasons/macos/Podfile.lock similarity index 100% rename from veggieseasons/macos/Podfile.lock rename to experimental/veggieseasons/macos/Podfile.lock diff --git a/veggieseasons/macos/Runner.xcodeproj/project.pbxproj b/experimental/veggieseasons/macos/Runner.xcodeproj/project.pbxproj similarity index 100% rename from veggieseasons/macos/Runner.xcodeproj/project.pbxproj rename to experimental/veggieseasons/macos/Runner.xcodeproj/project.pbxproj diff --git a/veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/experimental/veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to experimental/veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/experimental/veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to experimental/veggieseasons/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/veggieseasons/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/experimental/veggieseasons/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from veggieseasons/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to experimental/veggieseasons/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/veggieseasons/macos/Runner.xcworkspace/contents.xcworkspacedata b/experimental/veggieseasons/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from veggieseasons/macos/Runner.xcworkspace/contents.xcworkspacedata rename to experimental/veggieseasons/macos/Runner.xcworkspace/contents.xcworkspacedata diff --git a/veggieseasons/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/experimental/veggieseasons/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from veggieseasons/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to experimental/veggieseasons/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/veggieseasons/macos/Runner/AppDelegate.swift b/experimental/veggieseasons/macos/Runner/AppDelegate.swift similarity index 100% rename from veggieseasons/macos/Runner/AppDelegate.swift rename to experimental/veggieseasons/macos/Runner/AppDelegate.swift diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to experimental/veggieseasons/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/veggieseasons/macos/Runner/Base.lproj/MainMenu.xib b/experimental/veggieseasons/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from veggieseasons/macos/Runner/Base.lproj/MainMenu.xib rename to experimental/veggieseasons/macos/Runner/Base.lproj/MainMenu.xib diff --git a/veggieseasons/macos/Runner/Configs/AppInfo.xcconfig b/experimental/veggieseasons/macos/Runner/Configs/AppInfo.xcconfig similarity index 100% rename from veggieseasons/macos/Runner/Configs/AppInfo.xcconfig rename to experimental/veggieseasons/macos/Runner/Configs/AppInfo.xcconfig diff --git a/veggieseasons/macos/Runner/Configs/Debug.xcconfig b/experimental/veggieseasons/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from veggieseasons/macos/Runner/Configs/Debug.xcconfig rename to experimental/veggieseasons/macos/Runner/Configs/Debug.xcconfig diff --git a/veggieseasons/macos/Runner/Configs/Release.xcconfig b/experimental/veggieseasons/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from veggieseasons/macos/Runner/Configs/Release.xcconfig rename to experimental/veggieseasons/macos/Runner/Configs/Release.xcconfig diff --git a/veggieseasons/macos/Runner/Configs/Warnings.xcconfig b/experimental/veggieseasons/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from veggieseasons/macos/Runner/Configs/Warnings.xcconfig rename to experimental/veggieseasons/macos/Runner/Configs/Warnings.xcconfig diff --git a/veggieseasons/macos/Runner/DebugProfile.entitlements b/experimental/veggieseasons/macos/Runner/DebugProfile.entitlements similarity index 100% rename from veggieseasons/macos/Runner/DebugProfile.entitlements rename to experimental/veggieseasons/macos/Runner/DebugProfile.entitlements diff --git a/veggieseasons/macos/Runner/Info.plist b/experimental/veggieseasons/macos/Runner/Info.plist similarity index 100% rename from veggieseasons/macos/Runner/Info.plist rename to experimental/veggieseasons/macos/Runner/Info.plist diff --git a/veggieseasons/macos/Runner/MainFlutterWindow.swift b/experimental/veggieseasons/macos/Runner/MainFlutterWindow.swift similarity index 100% rename from veggieseasons/macos/Runner/MainFlutterWindow.swift rename to experimental/veggieseasons/macos/Runner/MainFlutterWindow.swift diff --git a/veggieseasons/macos/Runner/Release.entitlements b/experimental/veggieseasons/macos/Runner/Release.entitlements similarity index 100% rename from veggieseasons/macos/Runner/Release.entitlements rename to experimental/veggieseasons/macos/Runner/Release.entitlements diff --git a/veggieseasons/pubspec.lock b/experimental/veggieseasons/pubspec.lock similarity index 90% rename from veggieseasons/pubspec.lock rename to experimental/veggieseasons/pubspec.lock index ac91a1aa1..da7c00855 100644 --- a/veggieseasons/pubspec.lock +++ b/experimental/veggieseasons/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0-nullsafety.2" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.2" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.4" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.2" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.2" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0-nullsafety.4" convert: dependency: transitive description: @@ -84,7 +84,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.2" file: dependency: transitive description: @@ -135,20 +135,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.16.1" + js: + dependency: transitive + description: + name: js + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.3-nullsafety.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10-nullsafety.2" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.5" nested: dependency: transitive description: @@ -162,7 +169,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0-nullsafety.2" path_provider_linux: dependency: transitive description: @@ -265,56 +272,56 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0-nullsafety.3" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0-nullsafety.5" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.2" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.2" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.2" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19-nullsafety.4" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.4" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.4" xdg_directories: dependency: transitive description: @@ -337,5 +344,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.10.0-110 <2.11.0" + dart: ">=2.11.0-0.0 <=2.12.0-3.0.dev" flutter: ">=1.16.0 <2.0.0" diff --git a/veggieseasons/pubspec.yaml b/experimental/veggieseasons/pubspec.yaml similarity index 100% rename from veggieseasons/pubspec.yaml rename to experimental/veggieseasons/pubspec.yaml diff --git a/experimental/veggieseasons/test/restoration_test.dart b/experimental/veggieseasons/test/restoration_test.dart new file mode 100644 index 000000000..68ec3e982 --- /dev/null +++ b/experimental/veggieseasons/test/restoration_test.dart @@ -0,0 +1,166 @@ +// 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 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:veggieseasons/data/app_state.dart'; +import 'package:veggieseasons/data/veggie.dart'; +import 'package:veggieseasons/main.dart'; + +void main() { + testWidgets('restoration smoke test', (tester) async { + SharedPreferences.setMockInitialValues({}); + AppState.debugCurrentSeason = Season.autumn; + + await tester.pumpWidget( + RootRestorationScope( + restorationId: 'root', + child: VeggieApp(), + ), + ); + + expect(find.text('In season today'), findsOneWidget); + expect(find.text('Grapes'), findsNothing); + expect(find.text('Figs'), findsNothing); + await tester.scrollUntilVisible(find.text('Grapes'), 100); + expect(find.text('In season today'), findsNothing); + expect(find.text('Figs'), findsOneWidget); + expect(find.text('Grapes'), findsOneWidget); + + // Scroll offset of "Home" is restored. + await tester.restartAndRestore(); + expect(find.text('In season today'), findsNothing); + expect(find.text('Figs'), findsOneWidget); + expect(find.text('Grapes'), findsOneWidget); + + // Open details page for "Figs". + await tester.tap(find.text('Figs')); + await tester.pumpAndSettle(); + + expect(find.text('Grapes'), findsNothing); + expect(find.text('Figs'), findsOneWidget); + expect(find.text('Serving info'), findsOneWidget); + expect(tester.widget(find.byType(CupertinoSwitch)).value, isFalse); + await tester.tap(find.byType(CupertinoSwitch)); + await tester.pumpAndSettle(); + expect(tester.widget(find.byType(CupertinoSwitch)).value, isTrue); + + // Current details page is restored. + await tester.restartAndRestore(); + expect(find.text('Grapes'), findsNothing); + expect(find.text('Figs'), findsOneWidget); + expect(find.text('Serving info'), findsOneWidget); + expect(tester.widget(find.byType(CupertinoSwitch)).value, isTrue); + + await tester.tap(find.text('Trivia')); + await tester.pumpAndSettle(); + expect(find.text('Serving info'), findsNothing); + expect(find.text("Which of these isn't a variety of figs?"), findsOneWidget); + + // Restores to trivia page. + await tester.restartAndRestore(); + expect(find.text('Serving info'), findsNothing); + expect(find.text("Which of these isn't a variety of figs?"), findsOneWidget); + + await tester.tap(find.text('Brown Turkey')); + await tester.pumpAndSettle(); + expect(find.text("Which of these isn't a variety of figs?"), findsNothing); + expect(find.text('Next Question'), findsOneWidget); + + // Restores trivia state. + await tester.restartAndRestore(); + expect(find.text("Which of these isn't a variety of figs?"), findsNothing); + expect(find.text('Next Question'), findsOneWidget); + + // Close details page. + tester.state(find.byType(Navigator).last).pop(); + await tester.pumpAndSettle(); + expect(find.text('Trivia'), findsNothing); + + // Old scroll offset is still preserved. + expect(find.text('Grapes'), findsOneWidget); + expect(find.text('Figs'), findsOneWidget); + + await tester.restartAndRestore(); + expect(find.text('Grapes'), findsOneWidget); + expect(find.text('Figs'), findsOneWidget); + + // Go to the garden. + await tester.tap(find.text('My Garden')); + await tester.pumpAndSettle(); + + expect(find.text('My Garden'), findsNWidgets(2)); // Name of the tap & title of page. + expect(find.text('Grapes'), findsNothing); + expect(find.text('Figs'), findsOneWidget); + + // Restores the current selected tab. + await tester.restartAndRestore(); + expect(find.text('My Garden'), findsNWidgets(2)); // Name of the tap & title of page. + expect(find.text('Grapes'), findsNothing); + expect(find.text('Figs'), findsOneWidget); + expect(find.text('Apples'), findsNothing); + + // Go to "Search". + await tester.tap(find.text('Search')); + await tester.pumpAndSettle(); + + expect(find.text('Apples'), findsOneWidget); + expect(find.text('Tangelo'), findsNothing); + await tester.enterText(find.byType(CupertinoTextField).hitTestable(), 'Tan'); + await tester.pumpAndSettle(); + expect(find.text('Apples'), findsNothing); + expect(find.text('Tangelo'), findsOneWidget); + expect(find.text('Tan').hitTestable(), findsOneWidget); + + // Restores search text and result. + await tester.restartAndRestore(); + expect(find.text('Apples'), findsNothing); + expect(find.text('Tangelo'), findsOneWidget); + expect(find.text('Tan').hitTestable(), findsOneWidget); // search text + + expect(find.text('Calorie Target'), findsNothing); + + // Go to "Settings". + await tester.tap(find.text('Settings')); + await tester.pumpAndSettle(); + expect(find.text('Calorie Target'), findsOneWidget); + + await tester.restartAndRestore(); + expect(find.text('Calorie Target'), findsOneWidget); + + expect(find.text('AVAILABLE CALORIE LEVELS'), findsNothing); + + // Go to calorie target selection. + await tester.tap(find.text('Calorie Target')); + await tester.pumpAndSettle(); + expect(find.text('AVAILABLE CALORIE LEVELS'), findsOneWidget); + + await tester.restartAndRestore(); + expect(find.text('AVAILABLE CALORIE LEVELS'), findsOneWidget); + + // Go back to settings main screen. + tester.state(find.byType(Navigator).last).pop(); + await tester.pumpAndSettle(); + expect(find.text('AVAILABLE CALORIE LEVELS'), findsNothing); + expect(find.text('Allium'), findsNothing); + + // Go to preferred categories selection. + await tester.tap(find.text('Preferred Categories')); + await tester.pumpAndSettle(); + expect(find.text('Calorie Target'), findsNothing); + expect(find.text('Allium'), findsOneWidget); + + await tester.restartAndRestore(); + expect(find.text('Allium'), findsOneWidget); + + // Go back to settings main screen. + tester.state(find.byType(Navigator).last).pop(); + await tester.pumpAndSettle(); + expect(find.text('Allium'), findsNothing); + expect(find.text('Preferred Categories'), findsOneWidget); + expect(find.text('Calorie Target'), findsOneWidget); + }); +} diff --git a/veggieseasons/test/widget_test.dart b/experimental/veggieseasons/test/widget_test.dart similarity index 100% rename from veggieseasons/test/widget_test.dart rename to experimental/veggieseasons/test/widget_test.dart diff --git a/veggieseasons/web/icons/Icon-192.png b/experimental/veggieseasons/web/icons/Icon-192.png similarity index 100% rename from veggieseasons/web/icons/Icon-192.png rename to experimental/veggieseasons/web/icons/Icon-192.png diff --git a/veggieseasons/web/icons/Icon-512.png b/experimental/veggieseasons/web/icons/Icon-512.png similarity index 100% rename from veggieseasons/web/icons/Icon-512.png rename to experimental/veggieseasons/web/icons/Icon-512.png diff --git a/veggieseasons/web/index.html b/experimental/veggieseasons/web/index.html similarity index 100% rename from veggieseasons/web/index.html rename to experimental/veggieseasons/web/index.html diff --git a/veggieseasons/web/manifest.json b/experimental/veggieseasons/web/manifest.json similarity index 100% rename from veggieseasons/web/manifest.json rename to experimental/veggieseasons/web/manifest.json diff --git a/tool/travis_flutter_script.sh b/tool/travis_flutter_script.sh index 566389402..602db5632 100755 --- a/tool/travis_flutter_script.sh +++ b/tool/travis_flutter_script.sh @@ -42,7 +42,8 @@ declare -ar PROJECT_NAMES=( "provider_counter" \ "provider_shopper" \ "testing_app" \ - "veggieseasons" \ +# TODO(goderbauer): Add veggieseasons back when moved out of experimental again. +# "veggieseasons" \ ) for PROJECT_NAME in "${PROJECT_NAMES[@]}" diff --git a/veggieseasons/README.md b/veggieseasons/README.md index 114b3563a..275824271 100644 --- a/veggieseasons/README.md +++ b/veggieseasons/README.md @@ -1,42 +1,15 @@ # Veggie Seasons -An iOS app that shows which fruits and vegetables are currently in season. It -showcases Flutter's Cupertino package. +The Veggie Seasons app has temporarily been moved to the +[experimental](https://github.com/flutter/samples/tree/master/experimental/veggieseasons) +directory of this repository due to the integration of state restoration into +the app. Not all features of state restoration are available on the stable +channel of flutter yet. Once commit +[flutter/flutter@053ebf2](https://github.com/flutter/flutter/commit/053ebf2c080c7b8efbf4020683a5ba27d9daa3b8) +has reached the stable channel, the app can move back to its old home in +this directory. -[Available now in the App Store!](https://itunes.apple.com/is/app/veggie-seasons/id1450855435) - -**NOTE:** While Flutter supports many platforms, this application is designed -specifically for iOS. It's not intended to be run on Android, web, or desktop. - -## Goals - -* Show how to build an interface that iOS users will feel right at home - with. -* Show how Flutter's Cupertino widgets work together. - -## The important bits - -### `/screens/*` - -These are the screens presented in the app, roughly analogous to -UIViewControllers. `HomeScreen` is the root, and the others are shown -as the user navigates. - -### `/widgets/search_bar.dart` - -An example of how to construct an Cupertino-style search bar. The -Flutter team [is working on an official widget](https://github.com/flutter/flutter/issues/9784) -for this. Once that effort is complete, developers will not need to roll -their own search bars, so to speak. - -## Questions/issues - -If you have a general question about any of the techniques you see in -the sample, the best places to go are: - -* [The FlutterDev Google Group](https://groups.google.com/forum/#!forum/flutter-dev) -* [The Flutter Gitter channel](https://gitter.im/flutter/flutter) -* [StackOverflow](https://stackoverflow.com/questions/tagged/flutter) - -If you run into an issue with the sample itself, please file an issue -in the [main Flutter repo](https://github.com/flutter/flutter/issues). +For more info on how to run the samples in the `experimental` directory, +see the +[README](https://github.com/flutter/samples/tree/master/experimental/README.md) +in that folder. diff --git a/veggieseasons/lib/main.dart b/veggieseasons/lib/main.dart deleted file mode 100644 index bd334ee3a..000000000 --- a/veggieseasons/lib/main.dart +++ /dev/null @@ -1,35 +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 'package:flutter/services.dart' show DeviceOrientation, SystemChrome; -import 'package:provider/provider.dart'; -import 'package:veggieseasons/data/app_state.dart'; -import 'package:veggieseasons/data/preferences.dart'; -import 'package:veggieseasons/screens/home.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - SystemChrome.setPreferredOrientations([ - DeviceOrientation.portraitUp, - DeviceOrientation.portraitDown, - ]); - - runApp( - MultiProvider( - providers: [ - ChangeNotifierProvider( - create: (_) => AppState(), - ), - ChangeNotifierProvider( - create: (_) => Preferences()..load(), - ), - ], - child: CupertinoApp( - debugShowCheckedModeBanner: false, - home: HomeScreen(), - ), - ), - ); -} diff --git a/veggieseasons/lib/screens/home.dart b/veggieseasons/lib/screens/home.dart deleted file mode 100644 index cb01910c8..000000000 --- a/veggieseasons/lib/screens/home.dart +++ /dev/null @@ -1,47 +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 'package:flutter/widgets.dart'; -import 'package:veggieseasons/screens/favorites.dart'; -import 'package:veggieseasons/screens/list.dart'; -import 'package:veggieseasons/screens/search.dart'; -import 'package:veggieseasons/screens/settings.dart'; - -class HomeScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - return CupertinoTabScaffold( - tabBar: CupertinoTabBar(items: [ - BottomNavigationBarItem( - icon: Icon(CupertinoIcons.home), - label: 'Home', - ), - BottomNavigationBarItem( - icon: Icon(CupertinoIcons.book), - label: 'My Garden', - ), - BottomNavigationBarItem( - icon: Icon(CupertinoIcons.search), - label: 'Search', - ), - BottomNavigationBarItem( - icon: Icon(CupertinoIcons.settings), - label: 'Settings', - ), - ]), - tabBuilder: (context, index) { - if (index == 0) { - return ListScreen(); - } else if (index == 1) { - return FavoritesScreen(); - } else if (index == 2) { - return SearchScreen(); - } else { - return SettingsScreen(); - } - }, - ); - } -} diff --git a/veggieseasons/lib/screens/settings.dart b/veggieseasons/lib/screens/settings.dart deleted file mode 100644 index 912840f03..000000000 --- a/veggieseasons/lib/screens/settings.dart +++ /dev/null @@ -1,214 +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 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:provider/provider.dart'; -import 'package:veggieseasons/data/preferences.dart'; -import 'package:veggieseasons/data/veggie.dart'; -import 'package:veggieseasons/styles.dart'; -import 'package:veggieseasons/widgets/settings_group.dart'; -import 'package:veggieseasons/widgets/settings_item.dart'; - -class VeggieCategorySettingsScreen extends StatelessWidget { - @override - Widget build(BuildContext context) { - final model = Provider.of(context); - final currentPrefs = model.preferredCategories; - var brightness = CupertinoTheme.brightnessOf(context); - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text('Preferred Categories'), - previousPageTitle: 'Settings', - ), - backgroundColor: Styles.scaffoldBackground(brightness), - child: FutureBuilder>( - future: currentPrefs, - builder: (context, snapshot) { - final items = []; - - for (final category in VeggieCategory.values) { - CupertinoSwitch toggle; - - // It's possible that category data hasn't loaded from shared prefs - // yet, so display it if possible and fall back to disabled switches - // otherwise. - if (snapshot.hasData) { - toggle = CupertinoSwitch( - value: snapshot.data.contains(category), - onChanged: (value) { - if (value) { - model.addPreferredCategory(category); - } else { - model.removePreferredCategory(category); - } - }, - ); - } else { - toggle = CupertinoSwitch( - value: false, - onChanged: null, - ); - } - - items.add(SettingsItem( - label: veggieCategoryNames[category], - content: toggle, - )); - } - - return ListView( - children: [ - SettingsGroup( - items: items, - ), - ], - ); - }, - ), - ); - } -} - -class CalorieSettingsScreen extends StatelessWidget { - static const max = 1000; - static const min = 2600; - static const step = 200; - - @override - Widget build(BuildContext context) { - final model = Provider.of(context); - var brightness = CupertinoTheme.brightnessOf(context); - return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - previousPageTitle: 'Settings', - ), - backgroundColor: Styles.scaffoldBackground(brightness), - child: ListView( - children: [ - FutureBuilder( - future: model.desiredCalories, - builder: (context, snapshot) { - final steps = []; - - for (var cals = max; cals < min; cals += step) { - steps.add( - SettingsItem( - label: cals.toString(), - icon: SettingsIcon( - icon: Styles.checkIcon, - foregroundColor: snapshot.hasData && snapshot.data == cals - ? CupertinoColors.activeBlue - : Styles.transparentColor, - backgroundColor: Styles.transparentColor, - ), - onPress: snapshot.hasData - ? () => model.setDesiredCalories(cals) - : null, - ), - ); - } - - return SettingsGroup( - items: steps, - header: SettingsGroupHeader('Available calorie levels'), - footer: SettingsGroupFooter('These are used for serving ' - 'calculations'), - ); - }, - ), - ], - ), - ); - } -} - -class SettingsScreen extends StatelessWidget { - SettingsItem _buildCaloriesItem(BuildContext context, Preferences prefs) { - return SettingsItem( - label: 'Calorie Target', - icon: SettingsIcon( - backgroundColor: Styles.iconBlue, - icon: Styles.calorieIcon, - ), - content: FutureBuilder( - future: prefs.desiredCalories, - builder: (context, snapshot) { - return Row( - children: [ - Text( - snapshot.data?.toString() ?? '', - style: CupertinoTheme.of(context).textTheme.textStyle, - ), - SizedBox(width: 8), - SettingsNavigationIndicator(), - ], - ); - }, - ), - onPress: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (context) => CalorieSettingsScreen(), - title: 'Calorie Target', - ), - ); - }, - ); - } - - SettingsItem _buildCategoriesItem(BuildContext context, Preferences prefs) { - return SettingsItem( - label: 'Preferred Categories', - subtitle: 'What types of veggies you prefer!', - icon: SettingsIcon( - backgroundColor: Styles.iconGold, - icon: Styles.preferenceIcon, - ), - content: SettingsNavigationIndicator(), - onPress: () { - Navigator.of(context).push( - CupertinoPageRoute( - builder: (context) => VeggieCategorySettingsScreen(), - title: 'Preferred Categories', - ), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - final prefs = Provider.of(context); - - return CupertinoPageScaffold( - child: Container( - color: Styles.scaffoldBackground(CupertinoTheme.brightnessOf(context)), - child: CustomScrollView( - slivers: [ - CupertinoSliverNavigationBar( - largeTitle: Text('Settings'), - ), - SliverSafeArea( - top: false, - sliver: SliverList( - delegate: SliverChildListDelegate( - [ - SettingsGroup( - items: [ - _buildCaloriesItem(context, prefs), - _buildCategoriesItem(context, prefs), - ], - ), - ], - ), - ), - ), - ], - ), - ), - ); - } -}