diff --git a/dev/tools/mega_gallery.dart b/dev/tools/mega_gallery.dart index 3540af75e2..bdeb4e010f 100644 --- a/dev/tools/mega_gallery.dart +++ b/dev/tools/mega_gallery.dart @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/// Make `n` copies of material_gallery. +/// Make `n` copies of flutter_gallery. import 'dart:io'; @@ -27,13 +27,13 @@ void main(List args) { ArgResults results = argParser.parse(args); if (results['help']) { - print('Generate n copies of material_gallery.\n'); + print('Generate n copies of flutter_gallery.\n'); print('usage: dart mega_gallery.dart '); print(argParser.usage); exit(0); } - Directory source = new Directory(_normalize('examples/material_gallery')); + Directory source = new Directory(_normalize('examples/flutter_gallery')); Directory out = new Directory(_normalize(results['out'])); if (results['delete']) { @@ -55,10 +55,10 @@ void main(List args) { print('Stats:'); print(' packages/flutter : ${getStatsFor(new Directory("packages/flutter"))}'); - print(' examples/material_gallery : ${getStatsFor(new Directory("examples/material_gallery"))}'); + print(' examples/flutter_gallery : ${getStatsFor(new Directory("examples/flutter_gallery"))}'); print(''); - print('Making $copies copies of material_gallery:'); + print('Making $copies copies of flutter_gallery:'); Directory lib = _dir(out, 'lib'); if (lib.existsSync()) diff --git a/examples/README.md b/examples/README.md index 4cbd73a110..3182f7484e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -13,7 +13,7 @@ Available examples include: - **Hello, world** The [hello world app](hello_world) is a basic app that shows the text "hello, world." -- **Material Gallery** The [material gallery app](material_gallery) showcases +- **Flutter gallery** The [flutter gallery app](flutter_gallery) showcases Flutter's implementation of [material design](https://www.google.com/design/spec/material-design/introduction.html). - **Layers** The [layers vignettes](layers) show how to use the various layers diff --git a/examples/material_gallery/.gitignore b/examples/flutter_gallery/.gitignore similarity index 100% rename from examples/material_gallery/.gitignore rename to examples/flutter_gallery/.gitignore diff --git a/examples/material_gallery/README.md b/examples/flutter_gallery/README.md similarity index 80% rename from examples/material_gallery/README.md rename to examples/flutter_gallery/README.md index bb62601cd2..0d13d4db89 100644 --- a/examples/material_gallery/README.md +++ b/examples/flutter_gallery/README.md @@ -1,6 +1,6 @@ -# Flutter Material Gallery +# Flutter gallery -Demo app for the Material Design widgets implemented in Flutter. +Demo app for the Material Design widgets and other features provided by Flutter. ## Icon diff --git a/examples/flutter_gallery/android/AndroidManifest.xml b/examples/flutter_gallery/android/AndroidManifest.xml new file mode 100644 index 0000000000..2024e84000 --- /dev/null +++ b/examples/flutter_gallery/android/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + diff --git a/examples/material_gallery/android/res/mipmap-hdpi/ic_launcher.png b/examples/flutter_gallery/android/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from examples/material_gallery/android/res/mipmap-hdpi/ic_launcher.png rename to examples/flutter_gallery/android/res/mipmap-hdpi/ic_launcher.png diff --git a/examples/material_gallery/android/res/mipmap-mdpi/ic_launcher.png b/examples/flutter_gallery/android/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from examples/material_gallery/android/res/mipmap-mdpi/ic_launcher.png rename to examples/flutter_gallery/android/res/mipmap-mdpi/ic_launcher.png diff --git a/examples/material_gallery/android/res/mipmap-xhdpi/ic_launcher.png b/examples/flutter_gallery/android/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from examples/material_gallery/android/res/mipmap-xhdpi/ic_launcher.png rename to examples/flutter_gallery/android/res/mipmap-xhdpi/ic_launcher.png diff --git a/examples/material_gallery/android/res/mipmap-xxhdpi/ic_launcher.png b/examples/flutter_gallery/android/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from examples/material_gallery/android/res/mipmap-xxhdpi/ic_launcher.png rename to examples/flutter_gallery/android/res/mipmap-xxhdpi/ic_launcher.png diff --git a/examples/material_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png b/examples/flutter_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from examples/material_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png rename to examples/flutter_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/examples/material_gallery/assets/flutter_logo.png b/examples/flutter_gallery/assets/flutter_logo.png similarity index 100% rename from examples/material_gallery/assets/flutter_logo.png rename to examples/flutter_gallery/assets/flutter_logo.png diff --git a/examples/material_gallery/assets/section_animation.png b/examples/flutter_gallery/assets/section_animation.png similarity index 100% rename from examples/material_gallery/assets/section_animation.png rename to examples/flutter_gallery/assets/section_animation.png diff --git a/examples/material_gallery/assets/section_components.png b/examples/flutter_gallery/assets/section_components.png similarity index 100% rename from examples/material_gallery/assets/section_components.png rename to examples/flutter_gallery/assets/section_components.png diff --git a/examples/material_gallery/assets/section_layout.png b/examples/flutter_gallery/assets/section_layout.png similarity index 100% rename from examples/material_gallery/assets/section_layout.png rename to examples/flutter_gallery/assets/section_layout.png diff --git a/examples/material_gallery/assets/section_patterns.png b/examples/flutter_gallery/assets/section_patterns.png similarity index 100% rename from examples/material_gallery/assets/section_patterns.png rename to examples/flutter_gallery/assets/section_patterns.png diff --git a/examples/material_gallery/assets/section_style.png b/examples/flutter_gallery/assets/section_style.png similarity index 100% rename from examples/material_gallery/assets/section_style.png rename to examples/flutter_gallery/assets/section_style.png diff --git a/examples/material_gallery/assets/section_usability.png b/examples/flutter_gallery/assets/section_usability.png similarity index 100% rename from examples/material_gallery/assets/section_usability.png rename to examples/flutter_gallery/assets/section_usability.png diff --git a/examples/material_gallery/flutter.yaml b/examples/flutter_gallery/flutter.yaml similarity index 98% rename from examples/material_gallery/flutter.yaml rename to examples/flutter_gallery/flutter.yaml index 9639d3b59b..7a2ca11994 100644 --- a/examples/material_gallery/flutter.yaml +++ b/examples/flutter_gallery/flutter.yaml @@ -1,4 +1,4 @@ -name: material_gallery +name: flutter_gallery uses-material-design: true assets: - assets/flutter_logo.png diff --git a/examples/material_gallery/ios/.gitignore b/examples/flutter_gallery/ios/.gitignore similarity index 100% rename from examples/material_gallery/ios/.gitignore rename to examples/flutter_gallery/ios/.gitignore diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png diff --git a/examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png similarity index 100% rename from examples/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png rename to examples/flutter_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png diff --git a/examples/material_gallery/ios/Info.plist b/examples/flutter_gallery/ios/Info.plist similarity index 97% rename from examples/material_gallery/ios/Info.plist rename to examples/flutter_gallery/ios/Info.plist index 69341571d3..c89ab01a94 100644 --- a/examples/material_gallery/ios/Info.plist +++ b/examples/flutter_gallery/ios/Info.plist @@ -11,7 +11,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - Material Gallery + Flutter gallery CFBundlePackageType APPL CFBundleShortVersionString diff --git a/examples/material_gallery/ios/LaunchScreen.storyboard b/examples/flutter_gallery/ios/LaunchScreen.storyboard similarity index 100% rename from examples/material_gallery/ios/LaunchScreen.storyboard rename to examples/flutter_gallery/ios/LaunchScreen.storyboard diff --git a/examples/material_gallery/lib/demo/all.dart b/examples/flutter_gallery/lib/demo/all.dart similarity index 100% rename from examples/material_gallery/lib/demo/all.dart rename to examples/flutter_gallery/lib/demo/all.dart diff --git a/examples/material_gallery/lib/demo/buttons_demo.dart b/examples/flutter_gallery/lib/demo/buttons_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/buttons_demo.dart rename to examples/flutter_gallery/lib/demo/buttons_demo.dart diff --git a/examples/material_gallery/lib/demo/cards_demo.dart b/examples/flutter_gallery/lib/demo/cards_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/cards_demo.dart rename to examples/flutter_gallery/lib/demo/cards_demo.dart diff --git a/examples/material_gallery/lib/demo/chip_demo.dart b/examples/flutter_gallery/lib/demo/chip_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/chip_demo.dart rename to examples/flutter_gallery/lib/demo/chip_demo.dart diff --git a/examples/material_gallery/lib/demo/colors_demo.dart b/examples/flutter_gallery/lib/demo/colors_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/colors_demo.dart rename to examples/flutter_gallery/lib/demo/colors_demo.dart diff --git a/examples/material_gallery/lib/demo/data_table_demo.dart b/examples/flutter_gallery/lib/demo/data_table_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/data_table_demo.dart rename to examples/flutter_gallery/lib/demo/data_table_demo.dart diff --git a/examples/material_gallery/lib/demo/date_picker_demo.dart b/examples/flutter_gallery/lib/demo/date_picker_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/date_picker_demo.dart rename to examples/flutter_gallery/lib/demo/date_picker_demo.dart diff --git a/examples/material_gallery/lib/demo/dialog_demo.dart b/examples/flutter_gallery/lib/demo/dialog_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/dialog_demo.dart rename to examples/flutter_gallery/lib/demo/dialog_demo.dart diff --git a/examples/material_gallery/lib/demo/drawing_demo.dart b/examples/flutter_gallery/lib/demo/drawing_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/drawing_demo.dart rename to examples/flutter_gallery/lib/demo/drawing_demo.dart diff --git a/examples/material_gallery/lib/demo/drop_down_demo.dart b/examples/flutter_gallery/lib/demo/drop_down_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/drop_down_demo.dart rename to examples/flutter_gallery/lib/demo/drop_down_demo.dart diff --git a/examples/flutter_gallery/lib/demo/fail.dart b/examples/flutter_gallery/lib/demo/fail.dart new file mode 100644 index 0000000000..049a1b161b --- /dev/null +++ b/examples/flutter_gallery/lib/demo/fail.dart @@ -0,0 +1,24 @@ + +import 'package:flutter/material.dart'; + +class LimitDemo extends StatelessWidget { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('LimitedBox test') + ), + body: new Block( + children: [ + new LimitedBox( + maxWidth: 100.0, + maxHeight: 100.0, + child: new Container( + decoration: new BoxDecoration(backgroundColor: const Color(0xFF00FF00)) + ) + ) + ] + ) + ); + } +} diff --git a/examples/material_gallery/lib/demo/fitness_demo.dart b/examples/flutter_gallery/lib/demo/fitness_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/fitness_demo.dart rename to examples/flutter_gallery/lib/demo/fitness_demo.dart diff --git a/examples/material_gallery/lib/demo/flexible_space_demo.dart b/examples/flutter_gallery/lib/demo/flexible_space_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/flexible_space_demo.dart rename to examples/flutter_gallery/lib/demo/flexible_space_demo.dart diff --git a/examples/material_gallery/lib/demo/full_screen_dialog_demo.dart b/examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/full_screen_dialog_demo.dart rename to examples/flutter_gallery/lib/demo/full_screen_dialog_demo.dart diff --git a/examples/material_gallery/lib/demo/grid_list_demo.dart b/examples/flutter_gallery/lib/demo/grid_list_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/grid_list_demo.dart rename to examples/flutter_gallery/lib/demo/grid_list_demo.dart diff --git a/examples/material_gallery/lib/demo/icons_demo.dart b/examples/flutter_gallery/lib/demo/icons_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/icons_demo.dart rename to examples/flutter_gallery/lib/demo/icons_demo.dart diff --git a/examples/material_gallery/lib/demo/leave_behind_demo.dart b/examples/flutter_gallery/lib/demo/leave_behind_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/leave_behind_demo.dart rename to examples/flutter_gallery/lib/demo/leave_behind_demo.dart diff --git a/examples/material_gallery/lib/demo/list_demo.dart b/examples/flutter_gallery/lib/demo/list_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/list_demo.dart rename to examples/flutter_gallery/lib/demo/list_demo.dart diff --git a/examples/material_gallery/lib/demo/menu_demo.dart b/examples/flutter_gallery/lib/demo/menu_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/menu_demo.dart rename to examples/flutter_gallery/lib/demo/menu_demo.dart diff --git a/examples/material_gallery/lib/demo/modal_bottom_sheet_demo.dart b/examples/flutter_gallery/lib/demo/modal_bottom_sheet_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/modal_bottom_sheet_demo.dart rename to examples/flutter_gallery/lib/demo/modal_bottom_sheet_demo.dart diff --git a/examples/material_gallery/lib/demo/overscroll_demo.dart b/examples/flutter_gallery/lib/demo/overscroll_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/overscroll_demo.dart rename to examples/flutter_gallery/lib/demo/overscroll_demo.dart diff --git a/examples/material_gallery/lib/demo/page_selector_demo.dart b/examples/flutter_gallery/lib/demo/page_selector_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/page_selector_demo.dart rename to examples/flutter_gallery/lib/demo/page_selector_demo.dart diff --git a/examples/material_gallery/lib/demo/persistent_bottom_sheet_demo.dart b/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/persistent_bottom_sheet_demo.dart rename to examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart diff --git a/examples/material_gallery/lib/demo/progress_indicator_demo.dart b/examples/flutter_gallery/lib/demo/progress_indicator_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/progress_indicator_demo.dart rename to examples/flutter_gallery/lib/demo/progress_indicator_demo.dart diff --git a/examples/material_gallery/lib/demo/scrollable_tabs_demo.dart b/examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/scrollable_tabs_demo.dart rename to examples/flutter_gallery/lib/demo/scrollable_tabs_demo.dart diff --git a/examples/material_gallery/lib/demo/scrolling_techniques_demo.dart b/examples/flutter_gallery/lib/demo/scrolling_techniques_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/scrolling_techniques_demo.dart rename to examples/flutter_gallery/lib/demo/scrolling_techniques_demo.dart diff --git a/examples/material_gallery/lib/demo/selection_controls_demo.dart b/examples/flutter_gallery/lib/demo/selection_controls_demo.dart similarity index 99% rename from examples/material_gallery/lib/demo/selection_controls_demo.dart rename to examples/flutter_gallery/lib/demo/selection_controls_demo.dart index 69936fd0a0..29213d7f2b 100644 --- a/examples/material_gallery/lib/demo/selection_controls_demo.dart +++ b/examples/flutter_gallery/lib/demo/selection_controls_demo.dart @@ -60,7 +60,7 @@ class _SelectionControlsDemoState extends State { ]; return new TabbedComponentDemoScaffold( - title: 'Selection Controls', + title: 'Selection controls', demos: demos ); } diff --git a/examples/material_gallery/lib/demo/slider_demo.dart b/examples/flutter_gallery/lib/demo/slider_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/slider_demo.dart rename to examples/flutter_gallery/lib/demo/slider_demo.dart diff --git a/examples/material_gallery/lib/demo/snack_bar_demo.dart b/examples/flutter_gallery/lib/demo/snack_bar_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/snack_bar_demo.dart rename to examples/flutter_gallery/lib/demo/snack_bar_demo.dart diff --git a/examples/material_gallery/lib/demo/tabs_demo.dart b/examples/flutter_gallery/lib/demo/tabs_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/tabs_demo.dart rename to examples/flutter_gallery/lib/demo/tabs_demo.dart diff --git a/examples/material_gallery/lib/demo/tabs_fab_demo.dart b/examples/flutter_gallery/lib/demo/tabs_fab_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/tabs_fab_demo.dart rename to examples/flutter_gallery/lib/demo/tabs_fab_demo.dart diff --git a/examples/material_gallery/lib/demo/text_field_demo.dart b/examples/flutter_gallery/lib/demo/text_field_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/text_field_demo.dart rename to examples/flutter_gallery/lib/demo/text_field_demo.dart diff --git a/examples/material_gallery/lib/demo/time_picker_demo.dart b/examples/flutter_gallery/lib/demo/time_picker_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/time_picker_demo.dart rename to examples/flutter_gallery/lib/demo/time_picker_demo.dart diff --git a/examples/material_gallery/lib/demo/tooltip_demo.dart b/examples/flutter_gallery/lib/demo/tooltip_demo.dart similarity index 96% rename from examples/material_gallery/lib/demo/tooltip_demo.dart rename to examples/flutter_gallery/lib/demo/tooltip_demo.dart index 815186491a..b9b41cba8a 100644 --- a/examples/material_gallery/lib/demo/tooltip_demo.dart +++ b/examples/flutter_gallery/lib/demo/tooltip_demo.dart @@ -11,14 +11,14 @@ const String _introText = class TooltipDemo extends StatelessWidget { - static const String routeName = '/tooltip'; + static const String routeName = '/tooltips'; @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); return new Scaffold( appBar: new AppBar( - title: new Text('Tooltip') + title: new Text('Tooltips') ), body: new Builder( builder: (BuildContext context) { diff --git a/examples/material_gallery/lib/demo/two_level_list_demo.dart b/examples/flutter_gallery/lib/demo/two_level_list_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/two_level_list_demo.dart rename to examples/flutter_gallery/lib/demo/two_level_list_demo.dart diff --git a/examples/material_gallery/lib/demo/typography_demo.dart b/examples/flutter_gallery/lib/demo/typography_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/typography_demo.dart rename to examples/flutter_gallery/lib/demo/typography_demo.dart diff --git a/examples/material_gallery/lib/demo/weather_demo.dart b/examples/flutter_gallery/lib/demo/weather_demo.dart similarity index 100% rename from examples/material_gallery/lib/demo/weather_demo.dart rename to examples/flutter_gallery/lib/demo/weather_demo.dart diff --git a/examples/material_gallery/lib/gallery/app.dart b/examples/flutter_gallery/lib/gallery/app.dart similarity index 99% rename from examples/material_gallery/lib/gallery/app.dart rename to examples/flutter_gallery/lib/gallery/app.dart index 0fb4dacee6..10b8758a9b 100644 --- a/examples/material_gallery/lib/gallery/app.dart +++ b/examples/flutter_gallery/lib/gallery/app.dart @@ -68,7 +68,7 @@ class GalleryAppState extends State { @override Widget build(BuildContext context) { return new MaterialApp( - title: 'Flutter Material Gallery', + title: 'Flutter Gallery', theme: _useLightTheme ? _kGalleryLightTheme : _kGalleryDarkTheme, showPerformanceOverlay: _showPerformanceOverlay, routes: kRoutes, diff --git a/examples/material_gallery/lib/gallery/demo.dart b/examples/flutter_gallery/lib/gallery/demo.dart similarity index 99% rename from examples/material_gallery/lib/gallery/demo.dart rename to examples/flutter_gallery/lib/gallery/demo.dart index b619910705..5ce8e0fad5 100644 --- a/examples/material_gallery/lib/gallery/demo.dart +++ b/examples/flutter_gallery/lib/gallery/demo.dart @@ -270,7 +270,7 @@ class FullScreenCodeDialogState extends State { icon: Icons.clear, onPressed: () { Navigator.pop(context); } ), - title: new Text('Example Code') + title: new Text('Example code') ), body: body ); diff --git a/examples/material_gallery/lib/gallery/drawer.dart b/examples/flutter_gallery/lib/gallery/drawer.dart similarity index 97% rename from examples/material_gallery/lib/gallery/drawer.dart rename to examples/flutter_gallery/lib/gallery/drawer.dart index 8b99e38d82..a5acc12035 100644 --- a/examples/material_gallery/lib/gallery/drawer.dart +++ b/examples/flutter_gallery/lib/gallery/drawer.dart @@ -32,7 +32,7 @@ class GalleryDrawer extends StatelessWidget { return new Drawer( child: new Block( children: [ - new DrawerHeader(child: new Text('Flutter Gallery')), + new DrawerHeader(child: new Text('Flutter gallery')), new DrawerItem( icon: Icons.brightness_5, onPressed: () { onThemeChanged(true); }, diff --git a/examples/material_gallery/lib/gallery/example_code.dart b/examples/flutter_gallery/lib/gallery/example_code.dart similarity index 100% rename from examples/material_gallery/lib/gallery/example_code.dart rename to examples/flutter_gallery/lib/gallery/example_code.dart diff --git a/examples/material_gallery/lib/gallery/example_code_parser.dart b/examples/flutter_gallery/lib/gallery/example_code_parser.dart similarity index 100% rename from examples/material_gallery/lib/gallery/example_code_parser.dart rename to examples/flutter_gallery/lib/gallery/example_code_parser.dart diff --git a/examples/material_gallery/lib/gallery/header.dart b/examples/flutter_gallery/lib/gallery/header.dart similarity index 100% rename from examples/material_gallery/lib/gallery/header.dart rename to examples/flutter_gallery/lib/gallery/header.dart diff --git a/examples/material_gallery/lib/gallery/home.dart b/examples/flutter_gallery/lib/gallery/home.dart similarity index 100% rename from examples/material_gallery/lib/gallery/home.dart rename to examples/flutter_gallery/lib/gallery/home.dart diff --git a/examples/material_gallery/lib/gallery/item.dart b/examples/flutter_gallery/lib/gallery/item.dart similarity index 100% rename from examples/material_gallery/lib/gallery/item.dart rename to examples/flutter_gallery/lib/gallery/item.dart diff --git a/examples/material_gallery/lib/gallery/syntax_highlighter.dart b/examples/flutter_gallery/lib/gallery/syntax_highlighter.dart similarity index 100% rename from examples/material_gallery/lib/gallery/syntax_highlighter.dart rename to examples/flutter_gallery/lib/gallery/syntax_highlighter.dart diff --git a/examples/material_gallery/lib/main.dart b/examples/flutter_gallery/lib/main.dart similarity index 100% rename from examples/material_gallery/lib/main.dart rename to examples/flutter_gallery/lib/main.dart diff --git a/examples/flutter_gallery/material_gallery/.gitignore b/examples/flutter_gallery/material_gallery/.gitignore new file mode 100644 index 0000000000..14c7d4c3f7 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +.atom/ +.idea +.packages +.pub/ +build/ +ios/.generated/ +packages +pubspec.lock diff --git a/examples/flutter_gallery/material_gallery/README.md b/examples/flutter_gallery/material_gallery/README.md new file mode 100644 index 0000000000..0d13d4db89 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/README.md @@ -0,0 +1,12 @@ +# Flutter gallery + +Demo app for the Material Design widgets and other features provided by Flutter. + +## Icon + +Icon was generated using Android Asset Studio: +https://romannurik.github.io/AndroidAssetStudio/icons-launcher.html#foreground.type=image&foreground.space.trim=0&foreground.space.pad=0.1&foreColor=607d8b%2C0&crop=0&backgroundShape=square&backColor=ffffff%2C100&effects=none + +From the Flutter Logo: +https://github.com/flutter/website/blob/master/_includes/logo.html +which appears to be CC-BY 4.0. diff --git a/examples/material_gallery/android/AndroidManifest.xml b/examples/flutter_gallery/material_gallery/android/AndroidManifest.xml similarity index 93% rename from examples/material_gallery/android/AndroidManifest.xml rename to examples/flutter_gallery/material_gallery/android/AndroidManifest.xml index 3e7f58dcd1..b76c2b44e3 100644 --- a/examples/material_gallery/android/AndroidManifest.xml +++ b/examples/flutter_gallery/material_gallery/android/AndroidManifest.xml @@ -8,7 +8,7 @@ - + diff --git a/examples/flutter_gallery/material_gallery/android/res/mipmap-hdpi/ic_launcher.png b/examples/flutter_gallery/material_gallery/android/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..f0a55cda78 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/android/res/mipmap-hdpi/ic_launcher.png differ diff --git a/examples/flutter_gallery/material_gallery/android/res/mipmap-mdpi/ic_launcher.png b/examples/flutter_gallery/material_gallery/android/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..eb98dc451d Binary files /dev/null and b/examples/flutter_gallery/material_gallery/android/res/mipmap-mdpi/ic_launcher.png differ diff --git a/examples/flutter_gallery/material_gallery/android/res/mipmap-xhdpi/ic_launcher.png b/examples/flutter_gallery/material_gallery/android/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..f1783dbbf3 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/android/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/examples/flutter_gallery/material_gallery/android/res/mipmap-xxhdpi/ic_launcher.png b/examples/flutter_gallery/material_gallery/android/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..46828a290a Binary files /dev/null and b/examples/flutter_gallery/material_gallery/android/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/examples/flutter_gallery/material_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png b/examples/flutter_gallery/material_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a5276715d9 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/android/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/flutter_logo.png b/examples/flutter_gallery/material_gallery/assets/flutter_logo.png new file mode 100644 index 0000000000..b795ed72d1 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/flutter_logo.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/section_animation.png b/examples/flutter_gallery/material_gallery/assets/section_animation.png new file mode 100644 index 0000000000..8ada8cf0c1 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/section_animation.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/section_components.png b/examples/flutter_gallery/material_gallery/assets/section_components.png new file mode 100644 index 0000000000..5fb17bd60f Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/section_components.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/section_layout.png b/examples/flutter_gallery/material_gallery/assets/section_layout.png new file mode 100644 index 0000000000..808a1fdf5b Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/section_layout.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/section_patterns.png b/examples/flutter_gallery/material_gallery/assets/section_patterns.png new file mode 100644 index 0000000000..c78cb8aecc Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/section_patterns.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/section_style.png b/examples/flutter_gallery/material_gallery/assets/section_style.png new file mode 100644 index 0000000000..3433fb2cea Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/section_style.png differ diff --git a/examples/flutter_gallery/material_gallery/assets/section_usability.png b/examples/flutter_gallery/material_gallery/assets/section_usability.png new file mode 100644 index 0000000000..e1ba0aa601 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/assets/section_usability.png differ diff --git a/examples/flutter_gallery/material_gallery/flutter.yaml b/examples/flutter_gallery/material_gallery/flutter.yaml new file mode 100644 index 0000000000..7a2ca11994 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/flutter.yaml @@ -0,0 +1,41 @@ +name: flutter_gallery +uses-material-design: true +assets: + - assets/flutter_logo.png + - assets/section_animation.png + - assets/section_style.png + - assets/section_layout.png + - assets/section_components.png + - assets/section_patterns.png + - assets/section_usability.png + - packages/flutter_gallery_assets/ali_connors.png + - packages/flutter_gallery_assets/sun.png + - packages/flutter_gallery_assets/clouds-0.png + - packages/flutter_gallery_assets/clouds-1.png + - packages/flutter_gallery_assets/ray.png + - packages/flutter_gallery_assets/sun.png + - packages/flutter_gallery_assets/weathersprites.json + - packages/flutter_gallery_assets/weathersprites.png + - packages/flutter_gallery_assets/icon-sun.png + - packages/flutter_gallery_assets/icon-rain.png + - packages/flutter_gallery_assets/icon-snow.png + - packages/flutter_gallery_assets/kangaroo_valley_safari.png + - packages/flutter_gallery_assets/top_10_australian_beaches.png + - packages/flutter_gallery_assets/jumpingjack.json + - packages/flutter_gallery_assets/jumpingjack.png + - packages/flutter_gallery_assets/grain.png + - packages/flutter_gallery_assets/fancylines.png + - packages/flutter_gallery_assets/landscape_0.jpg + - packages/flutter_gallery_assets/landscape_1.jpg + - packages/flutter_gallery_assets/landscape_2.jpg + - packages/flutter_gallery_assets/landscape_3.jpg + - packages/flutter_gallery_assets/landscape_4.jpg + - packages/flutter_gallery_assets/landscape_5.jpg + - packages/flutter_gallery_assets/landscape_6.jpg + - packages/flutter_gallery_assets/landscape_7.jpg + - packages/flutter_gallery_assets/landscape_8.jpg + - packages/flutter_gallery_assets/landscape_9.jpg + - packages/flutter_gallery_assets/landscape_10.jpg + - packages/flutter_gallery_assets/landscape_11.jpg + - packages/flutter_gallery_assets/shadow.png + - lib/gallery/example_code.dart diff --git a/examples/flutter_gallery/material_gallery/ios/.gitignore b/examples/flutter_gallery/material_gallery/ios/.gitignore new file mode 100644 index 0000000000..ecb842d705 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/ios/.gitignore @@ -0,0 +1 @@ +.generated/ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..363d1c4ce4 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,142 @@ +{ + "images" : [ + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-Small@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Small-40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-Small-40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-60@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-Small@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Small-40.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-Small-40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16@2x.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32@2x.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128@2x.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256@2x.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512@2x.png", + "scale" : "2x" + } + ] +} \ No newline at end of file diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 0000000000..9996f5e598 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 0000000000..7a543edbdd Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png new file mode 100644 index 0000000000..05a8268104 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png new file mode 100644 index 0000000000..bfbca28f5d Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png new file mode 100644 index 0000000000..c924b8e497 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png new file mode 100644 index 0000000000..3006f1d348 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png new file mode 100644 index 0000000000..5f7d22d364 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png new file mode 100644 index 0000000000..9996f5e598 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png new file mode 100644 index 0000000000..3433da1196 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 0000000000..f7f9f16d1b Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 0000000000..e9c23601cf Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png new file mode 100644 index 0000000000..dd5fccb0a9 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png new file mode 100644 index 0000000000..1909e27511 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png new file mode 100644 index 0000000000..40a701b514 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png new file mode 100644 index 0000000000..06c1a80560 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png new file mode 100644 index 0000000000..1909e27511 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png new file mode 100644 index 0000000000..69c96ef1e2 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png new file mode 100644 index 0000000000..06c1a80560 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png new file mode 100644 index 0000000000..0f94371460 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png new file mode 100644 index 0000000000..69c96ef1e2 Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png new file mode 100644 index 0000000000..1d6355f58c Binary files /dev/null and b/examples/flutter_gallery/material_gallery/ios/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png differ diff --git a/examples/flutter_gallery/material_gallery/ios/Info.plist b/examples/flutter_gallery/material_gallery/ios/Info.plist new file mode 100644 index 0000000000..c89ab01a94 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/ios/Info.plist @@ -0,0 +1,47 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + Runner + CFBundleIdentifier + io.flutter.materialgallery + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Flutter gallery + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/examples/flutter_gallery/material_gallery/ios/LaunchScreen.storyboard b/examples/flutter_gallery/material_gallery/ios/LaunchScreen.storyboard new file mode 100644 index 0000000000..78686cd075 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/ios/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/flutter_gallery/material_gallery/lib/demo/all.dart b/examples/flutter_gallery/material_gallery/lib/demo/all.dart new file mode 100644 index 0000000000..a2c41f923f --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/all.dart @@ -0,0 +1,37 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'buttons_demo.dart'; +export 'cards_demo.dart'; +export 'chip_demo.dart'; +export 'colors_demo.dart'; +export 'data_table_demo.dart'; +export 'date_picker_demo.dart'; +export 'dialog_demo.dart'; +export 'drawing_demo.dart'; +export 'drop_down_demo.dart'; +export 'fitness_demo.dart'; +export 'flexible_space_demo.dart'; +export 'grid_list_demo.dart'; +export 'icons_demo.dart'; +export 'leave_behind_demo.dart'; +export 'list_demo.dart'; +export 'menu_demo.dart'; +export 'modal_bottom_sheet_demo.dart'; +export 'overscroll_demo.dart'; +export 'page_selector_demo.dart'; +export 'persistent_bottom_sheet_demo.dart'; +export 'progress_indicator_demo.dart'; +export 'scrollable_tabs_demo.dart'; +export 'selection_controls_demo.dart'; +export 'slider_demo.dart'; +export 'snack_bar_demo.dart'; +export 'tabs_demo.dart'; +export 'tabs_fab_demo.dart'; +export 'text_field_demo.dart'; +export 'time_picker_demo.dart'; +export 'tooltip_demo.dart'; +export 'two_level_list_demo.dart'; +export 'typography_demo.dart'; +export 'weather_demo.dart'; diff --git a/examples/flutter_gallery/material_gallery/lib/demo/buttons_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/buttons_demo.dart new file mode 100644 index 0000000000..70b9922a31 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/buttons_demo.dart @@ -0,0 +1,196 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +import '../gallery/demo.dart'; + +const String _raisedText = + "# Raised buttons\n" + "Raised buttons add dimension to mostly flat layouts. They emphasize " + "functions on busy or wide spaces."; + +const String _raisedCode = 'buttons_raised'; + +const String _flatText = + "# Flat buttons\n" + "A flat button displays an ink splash on press " + "but does not lift. Use flat buttons on toolbars, in dialogs and " + "inline with padding"; + +const String _flatCode = 'buttons_flat'; + +const String _dropdownText = + "# Dropdown buttons\n" + "A dropdown button displays a menu that's used to select a value from a " + "small set of values. The button displays the current value and a down " + "arrow."; + +const String _dropdownCode = 'buttons_dropdown'; + +const String _iconText = + "IconButtons are appropriate for toggle buttons that allow a single choice to be " + "selected or deselected, such as adding or removing an item's star."; + +const String _iconCode = 'buttons_icon'; + +const String _actionText = + "# Floating action buttons\n" + "Floating action buttons are used for a promoted action. They are " + "distinguished by a circled icon floating above the UI and can have motion " + "behaviors that include morphing, launching, and a transferring anchor " + "point."; + +const String _actionCode = 'buttons_action'; + +class ButtonsDemo extends StatefulWidget { + static const String routeName = '/buttons'; + + @override + _ButtonsDemoState createState() => new _ButtonsDemoState(); +} + +class _ButtonsDemoState extends State { + @override + Widget build(BuildContext context) { + List demos = [ + new ComponentDemoTabData( + tabName: 'RAISED', + description: _raisedText, + widget: buildRaisedButton(), + exampleCodeTag: _raisedCode + ), + new ComponentDemoTabData( + tabName: 'FLAT', + description: _flatText, + widget: buildFlatButton(), + exampleCodeTag: _flatCode + ), + new ComponentDemoTabData( + tabName: 'DROPDOWN', + description: _dropdownText, + widget: buildDropdownButton(), + exampleCodeTag: _dropdownCode + ), + new ComponentDemoTabData( + tabName: 'ICON', + description: _iconText, + widget: buildIconButton(), + exampleCodeTag: _iconCode + ), + new ComponentDemoTabData( + tabName: 'ACTION', + description: _actionText, + widget: buildActionButton(), + exampleCodeTag: _actionCode + ), + ]; + + return new TabbedComponentDemoScaffold( + title: 'Buttons', + demos: demos + ); + } + + Widget buildRaisedButton() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new ButtonBar( + alignment: MainAxisAlignment.collapse, + children: [ + new RaisedButton( + child: new Text('RAISED BUTTON'), + onPressed: () { + // Perform some action + } + ), + new RaisedButton( + child: new Text('DISABLED') + ) + ] + ) + ); + } + + Widget buildFlatButton() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new ButtonBar( + alignment: MainAxisAlignment.collapse, + children: [ + new FlatButton( + child: new Text('FLAT BUTTON'), + onPressed: () { + // Perform some action + } + ), + new FlatButton( + child: new Text('DISABLED') + ) + ] + ) + ); + } + + String dropdownValue = 'Free'; + + Widget buildDropdownButton() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new DropDownButton( + value: dropdownValue, + onChanged: (String newValue) { + setState(() { + if (newValue != null) + dropdownValue = newValue; + }); + }, + items: ['One', 'Two', 'Free', 'Four'] + .map((String value) { + return new DropDownMenuItem( + value: value, + child: new Text(value)); + }) + .toList() + ) + ); + } + + bool iconButtonToggle = false; + + Widget buildIconButton() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new Row( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new IconButton( + icon: Icons.thumb_up, + onPressed: () { + setState(() => iconButtonToggle = !iconButtonToggle); + }, + color: iconButtonToggle ? Theme.of(context).primaryColor : null + ), + new IconButton( + icon: Icons.thumb_up + ) + ] + .map((Widget button) => new SizedBox(width: 64.0, height: 64.0, child: button)) + .toList() + ) + ); + } + + Widget buildActionButton() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new FloatingActionButton( + child: new Icon(icon: Icons.add), + onPressed: () { + // Perform some action + } + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/cards_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/cards_demo.dart new file mode 100644 index 0000000000..4ebb6cbdf0 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/cards_demo.dart @@ -0,0 +1,143 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +class TravelDestination { + const TravelDestination({ this.assetName, this.title, this.description }); + + final String assetName; + final String title; + final List description; + + bool get isValid => assetName != null && title != null && description?.length == 3; +} + +final List destinations = [ + const TravelDestination( + assetName: 'packages/flutter_gallery_assets/top_10_australian_beaches.png', + title: 'Top 10 Australian beaches', + description: const [ + 'Number 10', + 'Whitehaven Beach', + 'Whitsunday Island, Whitsunday Islands' + ] + ), + const TravelDestination( + assetName: 'packages/flutter_gallery_assets/kangaroo_valley_safari.png', + title: 'Kangaroo Valley Safari', + description: const [ + '2031 Moss Vale Road', + 'Kangaroo Valley 2577', + 'New South Wales' + ] + ) +]; + +class TravelDestinationItem extends StatelessWidget { + TravelDestinationItem({ Key key, this.destination }) : super(key: key) { + assert(destination != null && destination.isValid); + } + + static final double height = 328.0; + final TravelDestination destination; + + @override + Widget build(BuildContext context) { + ThemeData theme = Theme.of(context); + TextStyle titleStyle = theme.textTheme.headline.copyWith(color: Colors.white); + TextStyle descriptionStyle = theme.textTheme.subhead; + + return new SizedBox( + height: height, + child: new Card( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // photo and title + new SizedBox( + height: 184.0, + child: new Stack( + children: [ + new Positioned( + left: 0.0, + top: 0.0, + bottom: 0.0, + right: 0.0, + child: new AssetImage( + name: destination.assetName, + fit: ImageFit.cover + ) + ), + new Positioned( + bottom: 16.0, + left: 16.0, + child: new Text(destination.title, style: titleStyle) + ) + ] + ) + ), + // description and share/expore buttons + new Flexible( + child: new Padding( + padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0), + child: new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // three line description + new Text(destination.description[0], style: descriptionStyle), + new Text(destination.description[1], style: descriptionStyle), + new Text(destination.description[2], style: descriptionStyle), + ] + ) + ) + ), + // share, explore buttons + // TODO(abarth): The theme and the bar should be part of card. + new ButtonTheme.footer( + child: new ButtonBar( + alignment: MainAxisAlignment.start, + children: [ + new FlatButton( + child: new Text('SHARE'), + onPressed: () { /* do nothing */ } + ), + new FlatButton( + child: new Text('EXPLORE'), + onPressed: () { /* do nothing */ } + ), + ] + ) + ), + ] + ) + ) + ); + } +} + +class CardsDemo extends StatelessWidget { + static const String routeName = '/cards'; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Travel stream') + ), + body: new ScrollableList( + itemExtent: TravelDestinationItem.height, + padding: const EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0), + children: destinations.map((TravelDestination destination) { + return new Container( + margin: const EdgeInsets.only(bottom: 8.0), + child: new TravelDestinationItem(destination: destination) + ); + }) + .toList() + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/chip_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/chip_demo.dart new file mode 100644 index 0000000000..08b2ae87f1 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/chip_demo.dart @@ -0,0 +1,54 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class ChipDemo extends StatefulWidget { + static const String routeName = '/chip'; + + @override + _ChipDemoState createState() => new _ChipDemoState(); +} + +class _ChipDemoState extends State { + bool _showBananas = true; + + void _deleteBananas() { + setState(() { + _showBananas = false; + }); + } + + @override + Widget build(BuildContext context) { + List chips = [ + new Chip( + label: new Text('Apple') + ), + new Chip( + avatar: new CircleAvatar(child: new Text('B')), + label: new Text('Blueberry') + ), + ]; + + if (_showBananas) { + chips.add(new Chip( + label: new Text('Bananas'), + onDeleted: _deleteBananas + )); + } + + return new Scaffold( + appBar: new AppBar(title: new Text('Chips')), + body: new Block( + children: chips.map((Widget widget) { + return new Container( + height: 100.0, + child: new Center(child: widget) + ); + }).toList() + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/colors_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/colors_demo.dart new file mode 100644 index 0000000000..471b34913d --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/colors_demo.dart @@ -0,0 +1,138 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +const double kColorItemHeight = 48.0; + +class ColorSwatch { + const ColorSwatch({ this.name, this.colors, this.accentColors, this.threshold: 900}); + + final String name; + final Map colors; + final Map accentColors; + final int threshold; // titles for indices > threshold are white, otherwise black + + bool get isValid => this.name != null && this.colors != null && threshold != null; +} + +const List colorSwatches = const [ + const ColorSwatch(name: 'RED', colors: Colors.red, accentColors: Colors.redAccent, threshold: 300), + const ColorSwatch(name: 'PINK', colors: Colors.pink, accentColors: Colors.pinkAccent, threshold: 200), + const ColorSwatch(name: 'PURPLE', colors: Colors.purple, accentColors: Colors.purpleAccent, threshold: 200), + const ColorSwatch(name: 'DEEP PURPLE', colors: Colors.deepPurple, accentColors: Colors.deepPurpleAccent, threshold: 200), + const ColorSwatch(name: 'INDIGO', colors: Colors.indigo, accentColors: Colors.indigoAccent, threshold: 200), + const ColorSwatch(name: 'BLUE', colors: Colors.blue, accentColors: Colors.blueAccent, threshold: 400), + const ColorSwatch(name: 'LIGHT BLUE', colors: Colors.lightBlue, accentColors: Colors.lightBlueAccent, threshold: 500), + const ColorSwatch(name: 'CYAN', colors: Colors.cyan, accentColors: Colors.cyanAccent, threshold: 600), + const ColorSwatch(name: 'TEAL', colors: Colors.teal, accentColors: Colors.tealAccent, threshold: 400), + const ColorSwatch(name: 'GREEN', colors: Colors.green, accentColors: Colors.greenAccent, threshold: 500), + const ColorSwatch(name: 'LIGHT GREEN', colors: Colors.lightGreen, accentColors: Colors.lightGreenAccent, threshold: 600), + const ColorSwatch(name: 'LIME', colors: Colors.lime, accentColors: Colors.limeAccent, threshold: 800), + const ColorSwatch(name: 'YELLOW', colors: Colors.yellow, accentColors: Colors.yellowAccent), + const ColorSwatch(name: 'AMBER', colors: Colors.amber, accentColors: Colors.amberAccent), + const ColorSwatch(name: 'ORANGE', colors: Colors.orange, accentColors: Colors.orangeAccent, threshold: 700), + const ColorSwatch(name: 'DEEP ORANGE', colors: Colors.deepOrange, accentColors: Colors.deepOrangeAccent, threshold: 400), + const ColorSwatch(name: 'BROWN', colors: Colors.brown, threshold: 200), + const ColorSwatch(name: 'GREY', colors: Colors.grey, threshold: 500), + const ColorSwatch(name: 'BLUE GREY', colors: Colors.blueGrey, threshold: 500) +]; + + +class ColorItem extends StatelessWidget { + ColorItem({ Key key, this.index, this.color, this.prefix: '' }) : super(key: key) { + assert(index != null); + assert(color != null); + assert(prefix != null); + } + + final int index; + final Color color; + final String prefix; + + String colorString() => "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; + + @override + Widget build(BuildContext context) { + return new Container( + height: kColorItemHeight, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + decoration: new BoxDecoration(backgroundColor: color), + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Text('$prefix$index'), + new Text(colorString()) + ] + ) + ); + } +} + +class ColorSwatchTabView extends StatelessWidget { + ColorSwatchTabView({ Key key, this.swatch }) : super(key: key) { + assert(swatch != null && swatch.isValid); + } + + final ColorSwatch swatch; + final TextStyle blackTextStyle = Typography.black.body1; + final TextStyle whiteTextStyle = Typography.white.body1; + + @override + Widget build(BuildContext context) { + List colorItems = swatch.colors.keys.map((int index) { + return new DefaultTextStyle( + style: index > swatch.threshold ? whiteTextStyle : blackTextStyle, + child: new ColorItem(index: index, color: swatch.colors[index]) + ); + }) + .toList(); + + if (swatch.accentColors != null) { + colorItems.addAll(swatch.accentColors.keys.map((int index) { + return new DefaultTextStyle( + style: index > swatch.threshold ? whiteTextStyle : blackTextStyle, + child: new ColorItem(index: index, color: swatch.accentColors[index], prefix: 'A') + ); + }) + .toList()); + } + + return new ScrollableList( + itemExtent: kColorItemHeight, + children: colorItems + ); + } +} + +class ColorsDemo extends StatelessWidget { + static const String routeName = '/colors'; + + @override + Widget build(BuildContext context) { + return new TabBarSelection( + values: colorSwatches, + child: new Scaffold( + appBar: new AppBar( + elevation: 0, + title: new Text('Colors'), + tabBar: new TabBar( + isScrollable: true, + labels: new Map.fromIterable(colorSwatches, value: (ColorSwatch swatch) { + return new TabLabel(text: swatch.name); + }) + ) + ), + body: new TabBarView( + children: colorSwatches.map((ColorSwatch swatch) { + return new ColorSwatchTabView(swatch: swatch); + }) + .toList() + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/data_table_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/data_table_demo.dart new file mode 100644 index 0000000000..2b6a0a325e --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/data_table_demo.dart @@ -0,0 +1,147 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/rendering.dart'; + +class Desert { + Desert(this.name, this.calories, this.fat, this.carbs, this.protein, this.sodium, this.calcium, this.iron); + final String name; + final int calories; + final double fat; + final int carbs; + final double protein; + final int sodium; + final int calcium; + final int iron; + + bool selected = false; +} + +class DataTableDemo extends StatefulWidget { + static const String routeName = '/data-table'; + + @override + _DataTableDemoState createState() => new _DataTableDemoState(); +} + +class _DataTableDemoState extends State { + + int _sortColumnIndex; + bool _sortAscending = true; + + final List _deserts = [ + new Desert('Frozen yogurt', 159, 6.0, 24, 4.0, 87, 14, 1), + new Desert('Ice cream sandwich', 237, 9.0, 37, 4.3, 129, 8, 1), + new Desert('Eclair', 262, 16.0, 24, 6.0, 337, 6, 7), + new Desert('Cupcake', 305, 3.7, 67, 4.3, 413, 3, 8), + new Desert('Gingerbread', 356, 16.0, 49, 3.9, 327, 7, 16), + new Desert('Jelly bean', 375, 0.0, 94, 0.0, 50, 0, 0), + new Desert('Lollipop', 392, 0.2, 98, 0.0, 38, 0, 2), + new Desert('Honeycomb', 408, 3.2, 87, 6.5, 562, 0, 45), + new Desert('Donut', 452, 25.0, 51, 4.9, 326, 2, 22), + new Desert('KitKat', 518, 26.0, 65, 7.0, 54, 12, 6), + ]; + + void _sort/**/(Comparable getField(Desert d), int columnIndex, bool ascending) { + setState(() { + _deserts.sort((Desert a, Desert b) { + if (!ascending) { + final Desert c = a; + a = b; + b = c; + } + final Comparable aValue = getField(a); + final Comparable bValue = getField(b); + return Comparable.compare(aValue, bValue); + }); + _sortColumnIndex = columnIndex; + _sortAscending = ascending; + }); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Data tables')), + body: new Block( + children: [ + new Material( + child: new IntrinsicHeight( + child: new Block( + scrollDirection: Axis.horizontal, + children: [ + new DataTable( + sortColumnIndex: _sortColumnIndex, + sortAscending: _sortAscending, + columns: [ + new DataColumn( + label: new Text('Dessert (100g serving)'), + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.name, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Calories'), + tooltip: 'The total amount of food energy in the given serving size.', + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.calories, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Fat (g)'), + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.fat, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Carbs (g)'), + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.carbs, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Protein (g)'), + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.protein, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Sodium (mg)'), + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.sodium, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Calcium (%)'), + tooltip: 'The amount of calcium as a percentage of the recommended daily amount.', + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.calcium, columnIndex, ascending) + ), + new DataColumn( + label: new Text('Iron (%)'), + numeric: true, + onSort: (int columnIndex, bool ascending) => _sort/**/((Desert d) => d.iron, columnIndex, ascending) + ), + ], + rows: _deserts.map/**/((Desert desert) { + return new DataRow( + key: new ValueKey(desert), + selected: desert.selected, + onSelectChanged: (bool selected) { setState(() { desert.selected = selected; }); }, + cells: [ + new DataCell(new Text('${desert.name}')), + new DataCell(new Text('${desert.calories}')), + new DataCell(new Text('${desert.fat.toStringAsFixed(1)}')), + new DataCell(new Text('${desert.carbs}')), + new DataCell(new Text('${desert.protein.toStringAsFixed(1)}')), + new DataCell(new Text('${desert.sodium}')), + new DataCell(new Text('${desert.calcium}%')), + new DataCell(new Text('${desert.iron}%')), + ] + ); + }).toList(growable: false) + ) + ] + ) + ) + ) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/date_picker_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/date_picker_demo.dart new file mode 100644 index 0000000000..7244a2acd1 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/date_picker_demo.dart @@ -0,0 +1,52 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class DatePickerDemo extends StatefulWidget { + static const String routeName = '/date-picker'; + + @override + _DatePickerDemoState createState() => new _DatePickerDemoState(); +} + +class _DatePickerDemoState extends State { + DateTime _selectedDate = new DateTime.now(); + + Future _handleSelectDate() async { + DateTime picked = await showDatePicker( + context: context, + initialDate: _selectedDate, + firstDate: new DateTime(2015, 8), + lastDate: new DateTime(2101) + ); + if (picked != _selectedDate) { + setState(() { + _selectedDate = picked; + }); + } + } + + @override + Widget build(BuildContext context) { + return + new Scaffold( + appBar: new AppBar(title: new Text('Date picker')), + body: new Column( + children: [ + new Text(new DateFormat.yMMMd().format(_selectedDate)), + new SizedBox(height: 20.0), + new RaisedButton( + onPressed: _handleSelectDate, + child: new Text('SELECT DATE') + ), + ], + mainAxisAlignment: MainAxisAlignment.center + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/dialog_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/dialog_demo.dart new file mode 100644 index 0000000000..f514a82ad0 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/dialog_demo.dart @@ -0,0 +1,203 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +import 'full_screen_dialog_demo.dart'; + +enum DialogDemoAction { + cancel, + discard, + disagree, + agree, +} + +const String _alertWithoutTitleText = "Discard draft?"; + +const String _alertWithTitleText = + "Let Google help apps determine location. This means sending anyonmous location " + "data to Google, even when no apps are running."; + +class DialogDemoItem extends StatelessWidget { + DialogDemoItem({ Key key, this.icon, this.color, this.text, this.onPressed }) : super(key: key); + + final IconData icon; + final Color color; + final String text; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + return new InkWell( + onTap: onPressed, + child: new Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Icon( + size: 36.0, + icon: icon, + color: color + ), + new Padding( + padding: const EdgeInsets.only(left: 16.0), + child: new Text(text) + ) + ] + ) + ) + ); + } +} + +class DialogDemo extends StatefulWidget { + static const String routeName = '/dialog'; + + @override + DialogDemoState createState() => new DialogDemoState(); +} + +class DialogDemoState extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); + + void showDemoDialog/**/({ BuildContext context, Dialog dialog }) { + showDialog/**/( + context: context, + child: dialog + ) + .then((dynamic/*=T*/ value) { // The value passed to Navigator.pop() or null. + if (value != null) { + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text('You selected: $value') + )); + } + }); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final TextStyle dialogTextStyle = theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color); + + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar( + title: new Text('Dialogs') + ), + body: new Block( + padding: const EdgeInsets.symmetric(vertical: 24.0, horizontal: 72.0), + children: [ + new RaisedButton( + child: new Text('ALERT'), + onPressed: () { + showDemoDialog/**/( + context: context, + dialog: new Dialog( + content: new Text( + _alertWithoutTitleText, + style: dialogTextStyle + ), + actions: [ + new FlatButton( + child: new Text('CANCEL'), + onPressed: () { Navigator.pop(context, DialogDemoAction.cancel); } + ), + new FlatButton( + child: new Text('DISCARD'), + onPressed: () { Navigator.pop(context, DialogDemoAction.discard); } + ) + ] + ) + ); + } + ), + new RaisedButton( + child: new Text('ALERT WITH TITLE'), + onPressed: () { + showDemoDialog/**/( + context: context, + dialog: new Dialog( + title: new Text('Use Google\'s location service?'), + content: new Text( + _alertWithTitleText, + style: dialogTextStyle + ), + actions: [ + new FlatButton( + child: new Text('DISAGREE'), + onPressed: () { Navigator.pop(context, DialogDemoAction.disagree); } + ), + new FlatButton( + child: new Text('AGREE'), + onPressed: () { Navigator.pop(context, DialogDemoAction.agree); } + ) + ] + ) + ); + } + ), + new RaisedButton( + child: new Text('SIMPLE'), + onPressed: () { + showDemoDialog/**/( + context: context, + dialog: new Dialog( + title: new Text('Set backup account'), + content: new Column( + children: [ + new DialogDemoItem( + icon: Icons.account_circle, + color: theme.primaryColor, + text: 'username@gmail.com', + onPressed: () { Navigator.pop(context, 'username@gmail.com'); } + ), + new DialogDemoItem( + icon: Icons.account_circle, + color: theme.primaryColor, + text: 'user02@gmail.com', + onPressed: () { Navigator.pop(context, 'user02@gmail.com'); } + ), + new DialogDemoItem( + icon: Icons.add_circle, + text: 'add account', + color: theme.disabledColor + ) + ] + ) + ) + ); + } + ), + new RaisedButton( + child: new Text('CONFIRMATION'), + onPressed: () { + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 15, minute: 30) + ) + .then((TimeOfDay value) { // The value passed to Navigator.pop() or null. + if (value != null) { + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text('You selected: $value') + )); + } + }); + } + ), + new RaisedButton( + child: new Text('FULLSCREEN'), + onPressed: () { + Navigator.push(context, new MaterialPageRoute( + builder: (BuildContext context) => new FullScreenDialogDemo() + )); + } + ) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/drawing_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/drawing_demo.dart new file mode 100644 index 0000000000..9774d52f07 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/drawing_demo.dart @@ -0,0 +1,85 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import 'package:flutter_sprites/flutter_sprites.dart'; + +class DrawingDemo extends StatefulWidget { + static const String routeName = '/drawing'; + + @override + _DrawingDemoState createState() => new _DrawingDemoState(); +} + +class _DrawingDemoState extends State { + _LineDrawingNode _rootNode; + ImageMap _images; + + Future _loadAssets(AssetBundle bundle) async { + _images = new ImageMap(bundle); + await _images.load([ + 'packages/flutter_gallery_assets/fancylines.png' + ]); + } + + @override + void initState() { + super.initState(); + _loadAssets(DefaultAssetBundle.of(context)).then((_) { + setState(() { + _rootNode = new _LineDrawingNode(_images); + }); + }); + } + + @override + Widget build(BuildContext context) { + Widget body; + if (_rootNode == null) { + body = new Center( + child: new CircularProgressIndicator() + ); + } else { + body = new SpriteWidget(_rootNode, SpriteBoxTransformMode.nativePoints); + } + + return new Scaffold( + appBar: new AppBar( + title: new Text('Fancy lines') + ), + body: body + ); + } +} + +class _LineDrawingNode extends NodeWithSize { + _LineDrawingNode(this._images) : super(const Size(1024.0, 1024.0)) { + userInteractionEnabled = true; + } + + final ImageMap _images; + EffectLine _currentLine; + + @override + bool handleEvent(SpriteBoxEvent event) { + if (event.type == PointerDownEvent) { + _currentLine = new EffectLine( + texture: new Texture(_images['packages/flutter_gallery_assets/fancylines.png']), + colorSequence: new ColorSequence.fromStartAndEndColor(Colors.purple[500], Colors.purple[600]), + fadeAfterDelay: 3.0, + fadeDuration: 1.0 + ); + _currentLine.addPoint(event.boxPosition); + addChild(_currentLine); + } else if (event.type == PointerMoveEvent) { + _currentLine.addPoint(event.boxPosition); + } + + return true; + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/drop_down_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/drop_down_demo.dart new file mode 100644 index 0000000000..eeaa119434 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/drop_down_demo.dart @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class DropDownDemo extends StatefulWidget { + static const String routeName = '/dropdown'; + + @override + _DropDownDemoState createState() => new _DropDownDemoState(); +} + +class _DropDownDemoState extends State { + String _value = "Free"; + + List> buildItems() { + return ["One", "Two", "Free", "Four"].map((String value) { + return new DropDownMenuItem(value: value, child: new Text(value)); + }) + .toList(); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Drop-down button')), + body: new Center( + child: new DropDownButton( + items: buildItems(), + value: _value, + onChanged: (String newValue) { + setState(() { + if (newValue != null) + _value = newValue; + }); + } + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/fitness_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/fitness_demo.dart new file mode 100644 index 0000000000..45cc3bac1f --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/fitness_demo.dart @@ -0,0 +1,589 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:math' as math; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_sprites/flutter_sprites.dart'; + +ImageMap _images; +SpriteSheet _sprites; + +class FitnessDemo extends StatelessWidget { + FitnessDemo({ Key key }) : super(key: key); + + static const String routeName = '/fitness'; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Fitness') + ), + body: new _FitnessDemoContents() + ); + } +} + +class _FitnessDemoContents extends StatefulWidget { + _FitnessDemoContents({ Key key }) : super(key: key); + + @override + _FitnessDemoContentsState createState() => new _FitnessDemoContentsState(); +} + +class _FitnessDemoContentsState extends State<_FitnessDemoContents> { + + Future _loadAssets(AssetBundle bundle) async { + _images = new ImageMap(bundle); + await _images.load([ + 'packages/flutter_gallery_assets/jumpingjack.png', + ]); + + String json = await DefaultAssetBundle.of(context).loadString('packages/flutter_gallery_assets/jumpingjack.json'); + _sprites = new SpriteSheet(_images['packages/flutter_gallery_assets/jumpingjack.png'], json); + } + + @override + void initState() { + super.initState(); + + AssetBundle bundle = DefaultAssetBundle.of(context); + _loadAssets(bundle).then((_) { + setState(() { + _assetsLoaded = true; + workoutAnimation = new _WorkoutAnimationNode( + onPerformedJumpingJack: () { + setState(() { + _count += 1; + }); + }, + onSecondPassed: (int seconds) { + setState(() { + _time = seconds; + }); + } + ); + }); + }); + } + + bool _assetsLoaded = false; + int _count = 0; + int _time = 0; + int get kcal => (_count * 0.2).toInt(); + + _WorkoutAnimationNode workoutAnimation; + + @override + Widget build(BuildContext context) { + if (!_assetsLoaded) + return new Container(); + + Color buttonColor; + String buttonText; + VoidCallback onButtonPressed; + + if (workoutAnimation.workingOut) { + buttonColor = Colors.red[500]; + buttonText = "STOP WORKOUT"; + onButtonPressed = endWorkout; + } else { + buttonColor = Theme.of(context).primaryColor; + buttonText = "START WORKOUT"; + onButtonPressed = startWorkout; + } + + return new Material( + child: new Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new Flexible( + child: new Container( + decoration: new BoxDecoration(backgroundColor: Colors.grey[800]), + child: new SpriteWidget(workoutAnimation, SpriteBoxTransformMode.scaleToFit) + ) + ), + new Padding( + padding: new EdgeInsets.only(top: 20.0), + child: new Text('JUMPING JACKS', style: Theme.of(context).textTheme.title) + ), + new Padding( + padding: new EdgeInsets.only(top: 20.0, bottom: 20.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _createInfoPanelCell(Icons.accessibility, '$_count', 'COUNT'), + _createInfoPanelCell(Icons.timer, _formatSeconds(_time), 'TIME'), + _createInfoPanelCell(Icons.flash_on, '$kcal', 'KCAL') + ] + ) + ), + new Padding( + padding: new EdgeInsets.only(bottom: 16.0), + child: new SizedBox( + width: 300.0, + height: 72.0, + child: new RaisedButton ( + onPressed: onButtonPressed, + color: buttonColor, + child: new Text( + buttonText, + style: new TextStyle(color: Colors.white, fontSize: 20.0) + ) + ) + ) + ) + ] + ) + ); + } + + Widget _createInfoPanelCell(IconData icon, String value, String description) { + Color color; + if (workoutAnimation.workingOut) + color = Colors.black87; + else + color = Theme.of(context).disabledColor; + + return new Container( + width: 100.0, + child: new Center( + child: new Column( + children: [ + new Icon(icon: icon, size: 48.0, color: color), + new Text(value, style: new TextStyle(fontSize: 24.0, color: color)), + new Text(description, style: new TextStyle(color: color)) + ] + ) + ) + ); + } + + String _formatSeconds(int seconds) { + int minutes = seconds ~/ 60; + String secondsStr = "${seconds % 60}".padLeft(2, "0"); + return "$minutes:$secondsStr"; + } + + void startWorkout() { + setState(() { + _count = 0; + _time = 0; + workoutAnimation.start(); + }); + } + + void endWorkout() { + setState(() { + workoutAnimation.stop(); + + if (_count >= 3) { + showDialog( + context: context, + child: new Stack(children: [ + new _Fireworks(), + new Dialog( + title: new Text('Awesome workout'), + content: new Text('You have completed $_count jumping jacks. Good going!'), + actions: [ + new FlatButton( + child: new Text('SWEET'), + onPressed: () { Navigator.pop(context); } + ) + ] + ) + ]) + ); + } + }); + } +} + +typedef void _SecondPassedCallback(int seconds); + +class _WorkoutAnimationNode extends NodeWithSize { + _WorkoutAnimationNode({ + this.onPerformedJumpingJack, + this.onSecondPassed + }) : super(const Size(1024.0, 1024.0)) { + reset(); + + _progress = new _ProgressCircle(const Size(800.0, 800.0)); + _progress.pivot = const Point(0.5, 0.5); + _progress.position = const Point(512.0, 512.0); + addChild(_progress); + + _jumpingJack = new _JumpingJack((){ + onPerformedJumpingJack(); + }); + _jumpingJack.scale = 0.5; + _jumpingJack.position = const Point(512.0, 550.0); + addChild(_jumpingJack); + } + + final VoidCallback onPerformedJumpingJack; + final _SecondPassedCallback onSecondPassed; + + int seconds; + + bool workingOut; + + static const int _kTargetMillis = 1000 * 30; + int _startTimeMillis; + _ProgressCircle _progress; + _JumpingJack _jumpingJack; + + void reset() { + seconds = 0; + workingOut = false; + } + + void start() { + reset(); + _startTimeMillis = new DateTime.now().millisecondsSinceEpoch; + workingOut = true; + _jumpingJack.animateJumping(); + } + + void stop() { + workingOut = false; + _jumpingJack.neutralPose(); + } + + @override + void update(double dt) { + if (workingOut) { + int millis = new DateTime.now().millisecondsSinceEpoch - _startTimeMillis; + int newSeconds = (millis) ~/ 1000; + if (newSeconds != seconds) { + seconds = newSeconds; + onSecondPassed(seconds); + } + + _progress.value = millis / _kTargetMillis; + } else { + _progress.value = 0.0; + } + } +} + +class _ProgressCircle extends NodeWithSize { + _ProgressCircle(Size size, [this.value = 0.0]) : super(size); + + static const double _kTwoPI = math.PI * 2.0; + static const double _kEpsilon = .0000001; + static const double _kSweep = _kTwoPI - _kEpsilon; + + double value; + + @override + void paint(Canvas canvas) { + applyTransformForPivot(canvas); + + Paint circlePaint = new Paint() + ..color = Colors.white30 + ..strokeWidth = 24.0 + ..style = PaintingStyle.stroke; + + canvas.drawCircle( + new Point(size.width / 2.0, size.height / 2.0), + size.width / 2.0, + circlePaint + ); + + Paint pathPaint = new Paint() + ..color = Colors.purple[500] + ..strokeWidth = 25.0 + ..style = PaintingStyle.stroke; + + double angle = value.clamp(0.0, 1.0) * _kSweep; + Path path = new Path() + ..arcTo(Point.origin & size, -math.PI / 2.0, angle, false); + canvas.drawPath(path, pathPaint); + } +} + +class _JumpingJack extends Node { + _JumpingJack(VoidCallback onPerformedJumpingJack) { + left = new _JumpingJackSide(false, onPerformedJumpingJack); + right = new _JumpingJackSide(true, null); + addChild(left); + addChild(right); + } + + void animateJumping() { + left.animateJumping(); + right.animateJumping(); + } + + void neutralPose() { + left.neutralPosition(true); + right.neutralPosition(true); + } + + _JumpingJackSide left; + _JumpingJackSide right; +} + +class _JumpingJackSide extends Node { + _JumpingJackSide(bool right, this.onPerformedJumpingJack) { + // Torso and head + torso = _createPart('torso.png', const Point(512.0, 512.0)); + addChild(torso); + + head = _createPart('head.png', const Point(512.0, 160.0)); + torso.addChild(head); + + if (right) { + torso.opacity = 0.0; + head.opacity = 0.0; + torso.scaleX = -1.0; + } + + // Left side movable parts + upperArm = _createPart('upper-arm.png', const Point(445.0, 220.0)); + torso.addChild(upperArm); + lowerArm = _createPart('lower-arm.png', const Point(306.0, 200.0)); + upperArm.addChild(lowerArm); + hand = _createPart('hand.png', const Point(215.0, 127.0)); + lowerArm.addChild(hand); + upperLeg = _createPart('upper-leg.png', const Point(467.0, 492.0)); + torso.addChild(upperLeg); + lowerLeg = _createPart('lower-leg.png', const Point(404.0, 660.0)); + upperLeg.addChild(lowerLeg); + foot = _createPart('foot.png', const Point(380.0, 835.0)); + lowerLeg.addChild(foot); + + torso.setPivotAndPosition(Point.origin); + + neutralPosition(false); + } + + _JumpingJackPart torso; + _JumpingJackPart head; + _JumpingJackPart upperArm; + _JumpingJackPart lowerArm; + _JumpingJackPart hand; + _JumpingJackPart lowerLeg; + _JumpingJackPart upperLeg; + _JumpingJackPart foot; + + final VoidCallback onPerformedJumpingJack; + + _JumpingJackPart _createPart(String textureName, Point pivotPosition) { + return new _JumpingJackPart(_sprites[textureName], pivotPosition); + } + + void animateJumping() { + actions.stopAll(); + actions.run(new ActionSequence([ + _createPoseAction(null, 0, 0.5), + new ActionCallFunction(_animateJumpingLoop) + ])); + } + + void _animateJumpingLoop() { + actions.run(new ActionRepeatForever( + new ActionSequence([ + _createPoseAction(0, 1, 0.30), + _createPoseAction(1, 2, 0.30), + _createPoseAction(2, 1, 0.30), + _createPoseAction(1, 0, 0.30), + new ActionCallFunction(() { + if (onPerformedJumpingJack != null) + onPerformedJumpingJack(); + }) + ]) + )); + } + + void neutralPosition(bool animate) { + actions.stopAll(); + if (animate) { + actions.run(_createPoseAction(null, 1, 0.5)); + } else { + List d = _dataForPose(1); + upperArm.rotation = d[0]; + lowerArm.rotation = d[1]; + hand.rotation = d[2]; + upperLeg.rotation = d[3]; + lowerLeg.rotation = d[4]; + foot.rotation = d[5]; + torso.position = new Point(0.0, d[6]); + } + } + + ActionInterval _createPoseAction(int startPose, int endPose, double duration) { + List d0 = _dataForPose(startPose); + List d1 = _dataForPose(endPose); + + List tweens = [ + _tweenRotation(upperArm, d0[0], d1[0], duration), + _tweenRotation(lowerArm, d0[1], d1[1], duration), + _tweenRotation(hand, d0[2], d1[2], duration), + _tweenRotation(upperLeg, d0[3], d1[3], duration), + _tweenRotation(lowerLeg, d0[4], d1[4], duration), + _tweenRotation(foot, d0[5], d1[5], duration), + new ActionTween( + (Point a) => torso.position = a, + new Point(0.0, d0[6]), + new Point(0.0, d1[6]), + duration + ) + ]; + + return new ActionGroup(tweens); + } + + ActionTween _tweenRotation(_JumpingJackPart part, double r0, double r1, double duration) { + return new ActionTween( + (double a) => part.rotation = a, + r0, + r1, + duration + ); + } + + List _dataForPose(int pose) { + if (pose == null) + return _dataForCurrentPose(); + + if (pose == 0) { + return [ + -80.0, // Upper arm rotation + -30.0, // Lower arm rotation + -10.0, // Hand rotation + -15.0, // Upper leg rotation + 5.0, // Lower leg rotation + 15.0, // Foot rotation + 0.0 // Torso y offset + ]; + } else if (pose == 1) { + return [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -70.0 + ]; + } else { + return [ + 40.0, + 30.0, + 10.0, + 20.0, + -20.0, + 15.0, + 40.0 + ]; + } + } + + List _dataForCurrentPose() { + return [ + upperArm.rotation, + lowerArm.rotation, + hand.rotation, + upperLeg.rotation, + lowerLeg.rotation, + foot.rotation, + torso.position.y + ]; + } +} + +class _JumpingJackPart extends Sprite { + _JumpingJackPart(Texture texture, this.pivotPosition) : super(texture); + final Point pivotPosition; + + void setPivotAndPosition(Point newPosition) { + pivot = new Point(pivotPosition.x / 1024.0, pivotPosition.y / 1024.0); + position = newPosition; + + for (Node child in children) { + _JumpingJackPart subPart = child; + subPart.setPivotAndPosition( + new Point( + subPart.pivotPosition.x - pivotPosition.x, + subPart.pivotPosition.y - pivotPosition.y + ) + ); + } + } +} + +class _Fireworks extends StatefulWidget { + _Fireworks({ Key key }) : super(key: key); + + @override + _FireworksState createState() => new _FireworksState(); +} + +class _FireworksState extends State<_Fireworks> { + @override + void initState() { + super.initState(); + fireworks = new _FireworksNode(); + } + + _FireworksNode fireworks; + + @override + Widget build(BuildContext context) { + return new SpriteWidget(fireworks); + } +} + +class _FireworksNode extends NodeWithSize { + _FireworksNode() : super(const Size(1024.0, 1024.0)); + double _countDown = 0.0; + + @override + void update(double dt) { + if (_countDown <= 0.0) { + _addExplosion(); + _countDown = randomDouble(); + } + + _countDown -= dt; + } + + Color _randomExplosionColor() { + double rand = randomDouble(); + if (rand < 0.25) + return Colors.pink[200]; + else if (rand < 0.5) + return Colors.lightBlue[200]; + else if (rand < 0.75) + return Colors.purple[200]; + else + return Colors.cyan[200]; + } + + void _addExplosion() { + Color startColor = _randomExplosionColor(); + Color endColor = startColor.withAlpha(0); + + ParticleSystem system = new ParticleSystem( + _sprites['particle-0.png'], + numParticlesToEmit: 100, + emissionRate: 1000.0, + rotateToMovement: true, + startRotation: 90.0, + endRotation: 90.0, + speed: 100.0, + speedVar: 50.0, + startSize: 1.0, + startSizeVar: 0.5, + gravity: const Offset(0.0, 30.0), + colorSequence: new ColorSequence.fromStartAndEndColor(startColor, endColor) + ); + system.position = new Point(randomDouble() * 1024.0, randomDouble() * 1024.0); + addChild(system); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/flexible_space_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/flexible_space_demo.dart new file mode 100644 index 0000000000..d63b082144 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/flexible_space_demo.dart @@ -0,0 +1,204 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +class _ContactCategory extends StatelessWidget { + _ContactCategory({ Key key, this.icon, this.children }) : super(key: key); + + final IconData icon; + final List children; + + @override + Widget build(BuildContext context) { + return new Container( + padding: const EdgeInsets.symmetric(vertical: 16.0), + decoration: new BoxDecoration( + border: new Border(bottom: new BorderSide(color: Theme.of(context).dividerColor)) + ), + child: new DefaultTextStyle( + style: Theme.of(context).textTheme.subhead, + child: new Row( + children: [ + new SizedBox( + width: 72.0, + child: new Icon(icon: icon, color: Theme.of(context).primaryColor) + ), + new Flexible(child: new Column(children: children)) + ] + ) + ) + ); + } +} + +class _ContactItem extends StatelessWidget { + _ContactItem({ Key key, this.icon, this.lines }) : super(key: key) { + assert(lines.length > 1); + } + + final IconData icon; + final List lines; + + @override + Widget build(BuildContext context) { + List columnChildren = lines.sublist(0, lines.length - 1).map((String line) => new Text(line)).toList(); + columnChildren.add(new Text(lines.last, style: Theme.of(context).textTheme.caption)); + + List rowChildren = [ + new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: columnChildren + ) + ]; + if (icon != null) { + rowChildren.add(new SizedBox( + width: 72.0, + child: new Icon(icon: icon, color: Theme.of(context).disabledColor) + )); + } + return new Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: rowChildren + ) + ); + } +} + +class FlexibleSpaceDemo extends StatefulWidget { + static const String routeName = '/flexible-space'; + + @override + FlexibleSpaceDemoState createState() => new FlexibleSpaceDemoState(); +} + +class FlexibleSpaceDemoState extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); + final double _appBarHeight = 256.0; + AppBarBehavior _appBarBehavior = AppBarBehavior.scroll; + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + return new Theme( + data: new ThemeData( + brightness: ThemeBrightness.light, + primarySwatch: Colors.indigo + ), + child: new Scaffold( + key: _scaffoldKey, + appBarBehavior: _appBarBehavior, + appBar: new AppBar( + expandedHeight: _appBarHeight, + actions: [ + new IconButton( + icon: Icons.create, + tooltip: 'Search', + onPressed: () { + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text('Not supported.') + )); + } + ), + new PopupMenuButton( + onSelected: (AppBarBehavior value) { + setState(() { + _appBarBehavior = value; + }); + }, + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: AppBarBehavior.scroll, + child: new Text('Toolbar scrolls away') + ), + new PopupMenuItem( + value: AppBarBehavior.under, + child: new Text('Toolbar stays put') + ) + ] + ) + ], + flexibleSpace: new FlexibleSpaceBar( + title : new Text('Ali Connors'), + background: new AssetImage( + name: 'packages/flutter_gallery_assets/ali_connors.png', + fit: ImageFit.cover, + height: _appBarHeight + ) + ) + ), + body: new Block( + padding: new EdgeInsets.only(top: _appBarHeight + statusBarHeight), + children: [ + new _ContactCategory( + icon: Icons.call, + children: [ + new _ContactItem( + icon: Icons.message, + lines: [ + '(650) 555-1234', + 'Mobile' + ] + ), + new _ContactItem( + icon: Icons.message, + lines: [ + '(323) 555-6789', + 'Work' + ] + ) + ] + ), + new _ContactCategory( + icon: Icons.email, + children: [ + new _ContactItem( + lines: [ + 'ali_connors@example.com', + 'Personal' + ] + ), + new _ContactItem( + lines: [ + 'aliconnors@example.com', + 'Work' + ] + ) + ] + ), + new _ContactCategory( + icon: Icons.location_on, + children: [ + new _ContactItem( + lines: [ + '2000 Main Street', + 'San Francisco, CA', + 'Home' + ] + ), + new _ContactItem( + lines: [ + '1600 Amphitheater Parkway', + 'Mountain View, CA', + 'Work' + ] + ), + new _ContactItem( + lines: [ + '126 Severyns Ave', + 'Mountain View, CA', + 'Jet Travel' + ] + ) + ] + ) + ] + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/full_screen_dialog_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/full_screen_dialog_demo.dart new file mode 100644 index 0000000000..ecb7889814 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/full_screen_dialog_demo.dart @@ -0,0 +1,246 @@ +// Copyright 2016 The Chromium Authors. 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:intl/intl.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +// This demo is based on +// https://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs + +enum DismissDialogAction { + cancel, + discard, + save, +} + +class DateTimeItem extends StatelessWidget { + DateTimeItem({ Key key, DateTime dateTime, this.onChanged }) + : date = new DateTime(dateTime.year, dateTime.month, dateTime.day), + time = new TimeOfDay(hour: dateTime.hour, minute: dateTime.minute), + super(key: key) { + assert(onChanged != null); + } + + final DateTime date; + final TimeOfDay time; + final ValueChanged onChanged; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + + return new DefaultTextStyle( + style: theme.textTheme.subhead, + child: new Row( + children: [ + new Flexible( + child: new Container( + padding: const EdgeInsets.symmetric(vertical: 8.0), + decoration: new BoxDecoration( + border: new Border(bottom: new BorderSide(color: theme.dividerColor)) + ), + child: new InkWell( + onTap: () { + showDatePicker( + context: context, + initialDate: date, + firstDate: date.subtract(const Duration(days: 30)), + lastDate: date.add(const Duration(days: 30)) + ) + .then((DateTime value) { + onChanged(new DateTime(value.year, value.month, value.day, time.hour, time.minute)); + }); + }, + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + new Text(new DateFormat('EEE, MMM d yyyy').format(date)), + new Icon(icon: Icons.arrow_drop_down, color: Colors.black54), + ] + ) + ) + ) + ), + new Container( + margin: const EdgeInsets.only(left: 8.0), + padding: const EdgeInsets.symmetric(vertical: 8.0), + decoration: new BoxDecoration( + border: new Border(bottom: new BorderSide(color: theme.dividerColor)) + ), + child: new InkWell( + onTap: () { + showTimePicker( + context: context, + initialTime: time + ) + .then((TimeOfDay value) { + onChanged(new DateTime(date.year, date.month, date.day, value.hour, value.minute)); + }); + }, + child: new Row( + children: [ + new Text('$time'), + new Icon(icon: Icons.arrow_drop_down, color: Colors.black54), + ] + ) + ) + ) + ] + ) + ); + } +} + +class FullScreenDialogDemo extends StatefulWidget { + @override + FullScreenDialogDemoState createState() => new FullScreenDialogDemoState(); +} + +class FullScreenDialogDemoState extends State { + DateTime _fromDateTime = new DateTime.now(); + DateTime _toDateTime = new DateTime.now(); + bool _allDayValue = false; + bool _saveNeeded = false; + + void handleDismissButton(BuildContext context) { + if (!_saveNeeded) { + Navigator.pop(context, null); + return; + } + + final ThemeData theme = Theme.of(context); + final TextStyle dialogTextStyle = theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color); + + showDialog( + context: context, + child: new Dialog( + content: new Text( + 'Discard new event?', + style: dialogTextStyle + ), + actions: [ + new FlatButton( + child: new Text('CANCEL'), + onPressed: () { Navigator.pop(context, DismissDialogAction.cancel); } + ), + new FlatButton( + child: new Text('DISCARD'), + onPressed: () { + Navigator.openTransaction(context, (NavigatorTransaction transaction) { + transaction.pop(DismissDialogAction.discard); // pop the cancel/discard dialog + transaction.pop(null); // pop this route + }); + } + ) + ] + ) + ); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + + return new Scaffold( + appBar: new AppBar( + leading: new IconButton( + icon: Icons.clear, + onPressed: () { handleDismissButton(context); } + ), + title: new Text('New event'), + actions: [ + new FlatButton( + child: new Text('SAVE', style: theme.textTheme.body1.copyWith(color: Colors.white)), + onPressed: () { + Navigator.pop(context, DismissDialogAction.save); + } + ) + ] + ), + body: new Block( + padding: const EdgeInsets.all(16.0), + children: [ + new Container( + padding: const EdgeInsets.symmetric(vertical: 8.0), + decoration: new BoxDecoration( + border: new Border(bottom: new BorderSide(color: theme.dividerColor)) + ), + child: new Align( + alignment: FractionalOffset.bottomLeft, + child: new Text('Event name', style: theme.textTheme.display2) + ) + ), + new Container( + padding: const EdgeInsets.symmetric(vertical: 8.0), + decoration: new BoxDecoration( + border: new Border(bottom: new BorderSide(color: theme.dividerColor)) + ), + child: new Align( + alignment: FractionalOffset.bottomLeft, + child: new Text('Location', style: theme.textTheme.title.copyWith(color: Colors.black54)) + ) + ), + new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text('From', style: theme.textTheme.caption), + new DateTimeItem( + dateTime: _fromDateTime, + onChanged: (DateTime value) { + setState(() { + _fromDateTime = value; + _saveNeeded = true; + }); + } + ) + ] + ), + new Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Text('To', style: theme.textTheme.caption), + new DateTimeItem( + dateTime: _toDateTime, + onChanged: (DateTime value) { + setState(() { + _toDateTime = value; + _saveNeeded = true; + }); + } + ) + ] + ), + new Container( + decoration: new BoxDecoration( + border: new Border(bottom: new BorderSide(color: theme.dividerColor)) + ), + child: new Row( + children: [ + new Checkbox( + value: _allDayValue, + onChanged: (bool value) { + setState(() { + _allDayValue = value; + _saveNeeded = true; + }); + } + ), + new Text('All-day') + ] + ) + ) + ] + .map((Widget child) { + return new Container( + padding: const EdgeInsets.symmetric(vertical: 8.0), + height: 96.0, + child: child + ); + }) + .toList() + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/grid_list_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/grid_list_demo.dart new file mode 100644 index 0000000000..129be0913c --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/grid_list_demo.dart @@ -0,0 +1,283 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:collection'; + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../gallery/demo.dart'; + +const String _kExampleCode = 'gridlists'; + +enum GridDemoTileStyle { + imageOnly, + oneLine, + twoLine +} + +class Photo { + Photo({ this.assetName, this.title, this.caption, this.isFavorite: false }); + + final String assetName; + final String title; + final String caption; + + bool isFavorite; + + bool get isValid => assetName != null && title != null && caption != null && isFavorite != null; +} + +const String photoHeroTag = 'Photo'; + +typedef void PhotoFavoriteCallback(Photo photo); + +class GridDemoPhotoItem extends StatelessWidget { + GridDemoPhotoItem({ + Key key, + this.photo, + this.tileStyle, + this.onPressedFavorite + }) : super(key: key) { + assert(photo != null && photo.isValid); + assert(tileStyle != null); + assert(onPressedFavorite != null); + } + + final Photo photo; + final GridDemoTileStyle tileStyle; + final PhotoFavoriteCallback onPressedFavorite; + + void showPhoto(BuildContext context) { + Key photoKey = new Key(photo.assetName); + Set mostValuableKeys = new HashSet(); + mostValuableKeys.add(photoKey); + + Navigator.push(context, new MaterialPageRoute( + settings: new RouteSettings( + mostValuableKeys: mostValuableKeys + ), + builder: (BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text(photo.title) + ), + body: new Material( + child: new Hero( + tag: photoHeroTag, + child: new AssetImage( + name: photo.assetName, + fit: ImageFit.cover + ) + ) + ) + ); + } + )); + } + + @override + Widget build(BuildContext context) { + final Widget image = new GestureDetector( + onTap: () { showPhoto(context); }, + child: new Hero( + key: new Key(photo.assetName), + tag: photoHeroTag, + child: new AssetImage( + name: photo.assetName, + fit: ImageFit.cover + ) + ) + ); + + IconData icon = photo.isFavorite ? Icons.star : Icons.star_border; + + switch(tileStyle) { + case GridDemoTileStyle.imageOnly: + return image; + + case GridDemoTileStyle.oneLine: + return new GridTile( + header: new GridTileBar( + backgroundColor: Colors.black45, + leading: new IconButton( + icon: icon, + color: Colors.white, + onPressed: () { onPressedFavorite(photo); } + ), + title: new Text(photo.title) + ), + child: image + ); + + case GridDemoTileStyle.twoLine: + return new GridTile( + footer: new GridTileBar( + backgroundColor: Colors.black45, + title: new Text(photo.title), + subtitle: new Text(photo.caption), + trailing: new IconButton( + icon: icon, + color: Colors.white, + onPressed: () { onPressedFavorite(photo); } + ) + ), + child: image + ); + } + } +} + +class GridListDemo extends StatefulWidget { + GridListDemo({ Key key }) : super(key: key); + + static const String routeName = '/grid-list'; + + @override + GridListDemoState createState() => new GridListDemoState(); +} + +class GridListDemoState extends State { + GridDemoTileStyle tileStyle = GridDemoTileStyle.twoLine; + + List photos = [ + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_0.jpg', + title: 'Philippines', + caption: 'Batad rice terraces' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_1.jpg', + title: 'Italy', + caption: 'Ceresole Reale' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_2.jpg', + title: 'Somewhere', + caption: 'Beautiful mountains' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_3.jpg', + title: 'A place', + caption: 'Beautiful hills' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_4.jpg', + title: 'New Zealand', + caption: 'View from the van' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_5.jpg', + title: 'Autumn', + caption: 'The golden season' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_6.jpg', + title: 'Germany', + caption: 'Englischer Garten' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_7.jpg', + title: 'A country', + caption: 'Grass fields' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_8.jpg', + title: 'Mountain country', + caption: 'River forest' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_9.jpg', + title: 'Alpine place', + caption: 'Green hills' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_10.jpg', + title: 'Desert land', + caption: 'Blue skies' + ), + new Photo( + assetName: 'packages/flutter_gallery_assets/landscape_11.jpg', + title: 'Narnia', + caption: 'Rocks and rivers' + ), + ]; + + void showTileStyleMenu(BuildContext context) { + final List> items = >[ + new PopupMenuItem( + value: GridDemoTileStyle.imageOnly, + child: new Text('Image only') + ), + new PopupMenuItem( + value: GridDemoTileStyle.oneLine, + child: new Text('One line') + ), + new PopupMenuItem( + value: GridDemoTileStyle.twoLine, + child: new Text('Two line') + ) + ]; + + final EdgeInsets padding = MediaQuery.of(context).padding; + final RelativeRect position = new RelativeRect.fromLTRB( + 0.0, padding.top + 16.0, padding.right + 16.0, 0.0 + ); + + showMenu(context: context, position: position, items: items).then((GridDemoTileStyle value) { + setState(() { + tileStyle = value; + }); + }); + } + + // When the ScrollableGrid first appears we want the last row to only be + // partially visible, to help the user recognize that the grid is scrollable. + // The GridListDemoGridDelegate's tileHeightFactor is used for this. + @override + Widget build(BuildContext context) { + final Orientation orientation = MediaQuery.of(context).orientation; + return new Scaffold( + appBar: new AppBar( + title: new Text('Grid list'), + actions: [ + new IconButton( + icon: Icons.more_vert, + onPressed: () { showTileStyleMenu(context); }, + tooltip: 'Show menu' + ) + ] + ), + body: new Column( + children: [ + new Flexible( + child: new ScrollableGrid( + delegate: new FixedColumnCountGridDelegate( + columnCount: (orientation == Orientation.portrait) ? 2 : 3, + rowSpacing: 4.0, + columnSpacing: 4.0, + padding: const EdgeInsets.all(4.0), + tileAspectRatio: (orientation == Orientation.portrait) ? 1.0 : 1.3 + ), + children: photos.map((Photo photo) { + return new GridDemoPhotoItem( + photo: photo, + tileStyle: tileStyle, + onPressedFavorite: (Photo photo) { + setState(() { + photo.isFavorite = !photo.isFavorite; + }); + } + ); + }) + ) + ), + new DemoBottomBar( + exampleCodeTag: _kExampleCode + ) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/icons_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/icons_demo.dart new file mode 100644 index 0000000000..b83ef8c542 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/icons_demo.dart @@ -0,0 +1,162 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +class IconsDemo extends StatefulWidget { + static const String routeName = '/icons'; + + @override + IconsDemoState createState() => new IconsDemoState(); +} + +class IconsDemoState extends State { + static final List> iconColorSwatches = >[ + Colors.red, + Colors.pink, + Colors.purple, + Colors.deepPurple, + Colors.indigo, + Colors.blue, + Colors.lightBlue, + Colors.cyan, + Colors.teal, + Colors.green, + Colors.lightGreen, + Colors.lime, + Colors.yellow, + Colors.amber, + Colors.orange, + Colors.deepOrange, + Colors.brown, + Colors.grey, + Colors.blueGrey + ]; + + int iconColorIndex = 2; + double iconOpacity = 1.0; + + Color get iconColor => iconColorSwatches[iconColorIndex][400]; + + void handleIconButtonPress() { + setState(() { + iconColorIndex = (iconColorIndex + 1) % iconColorSwatches.length; + }); + } + + Widget buildIconButton(double size, IconData icon, bool enabled) { + return new IconButton( + size: size, + icon: icon, + color: iconColor, + tooltip: "${enabled ? 'enabled' : 'disabled'} icon button", + onPressed: enabled ? handleIconButtonPress : null + ); + } + + Widget buildSizeLabel(int size, TextStyle style) { + return new SizedBox( + height: size.toDouble() + 16.0, // to match an IconButton's padded height + child: new Center( + child: new Text('$size', style: style) + ) + ); + } + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final TextStyle textStyle = theme.textTheme.subhead.copyWith(color: theme.textTheme.caption.color); + + return new Scaffold( + appBar: new AppBar( + title: new Text('Icons') + ), + body: new IconTheme( + data: new IconThemeData(opacity: iconOpacity), + child: new Padding( + padding: const EdgeInsets.all(24.0), + child: new Column( + children: [ + new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Flexible( + flex: 0, + child: new Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Text('Size', style: textStyle), + buildSizeLabel(18, textStyle), + buildSizeLabel(24, textStyle), + buildSizeLabel(36, textStyle), + buildSizeLabel(48, textStyle) + ] + ) + ), + new Flexible( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Text('Enabled', style: textStyle), + buildIconButton(18.0, Icons.face, true), + buildIconButton(24.0, Icons.alarm, true), + buildIconButton(36.0, Icons.home, true), + buildIconButton(48.0, Icons.android, true) + ] + ) + ), + new Flexible( + child: new Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + new Text('Disabled', style: textStyle), + buildIconButton(18.0, Icons.face, false), + buildIconButton(24.0, Icons.alarm, false), + buildIconButton(36.0, Icons.home, false), + buildIconButton(48.0, Icons.android, false) + ] + ) + ) + ] + ), + new Flexible( + child: new Center( + child: new IconTheme( + data: new IconThemeData(opacity: 1.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new Icon( + icon: Icons.brightness_7, + color: iconColor.withAlpha(0x33) // 0.2 * 255 = 0x33 + ), + new Slider( + value: iconOpacity, + min: 0.2, + max: 1.0, + activeColor: iconColor, + onChanged: (double newValue) { + setState(() { + iconOpacity = newValue; + }); + } + ), + new Icon( + icon: Icons.brightness_7, + color: iconColor.withAlpha(0xFF) + ), + ] + ) + ) + ) + ) + ] + ) + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/leave_behind_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/leave_behind_demo.dart new file mode 100644 index 0000000000..9e4ed1880f --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/leave_behind_demo.dart @@ -0,0 +1,170 @@ +// Copyright 2016 The Chromium Authors. 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:collection/collection.dart' show lowerBound; + +import 'package:flutter/material.dart'; + +enum LeaveBehindDemoAction { + reset, + horizontalSwipe, + leftSwipe, + rightSwipe +} + +class LeaveBehindItem implements Comparable { + LeaveBehindItem({ this.index, this.name, this.subject, this.body }); + + LeaveBehindItem.from(LeaveBehindItem item) + : index = item.index, name = item.name, subject = item.subject, body = item.body; + + final int index; + final String name; + final String subject; + final String body; + + @override + int compareTo(LeaveBehindItem other) => index.compareTo(other.index); +} + +class LeaveBehindDemo extends StatefulWidget { + LeaveBehindDemo({ Key key }) : super(key: key); + + static const String routeName = '/leave-behind'; + + @override + LeaveBehindDemoState createState() => new LeaveBehindDemoState(); +} + +class LeaveBehindDemoState extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); + DismissDirection _dismissDirection = DismissDirection.horizontal; + List leaveBehindItems; + + void initListItems() { + leaveBehindItems = new List.generate(16, (int index) { + return new LeaveBehindItem( + index: index, + name: 'Item $index Sender', + subject: 'Subject: $index', + body: "[$index] first line of the message's body..." + ); + }); + } + + @override + void initState() { + super.initState(); + initListItems(); + } + + void handleDemoAction(LeaveBehindDemoAction action) { + switch(action) { + case LeaveBehindDemoAction.reset: + initListItems(); + break; + case LeaveBehindDemoAction.horizontalSwipe: + _dismissDirection = DismissDirection.horizontal; + break; + case LeaveBehindDemoAction.leftSwipe: + _dismissDirection = DismissDirection.endToStart; + break; + case LeaveBehindDemoAction.rightSwipe: + _dismissDirection = DismissDirection.startToEnd; + break; + } + } + + void handleUndo(LeaveBehindItem item) { + int insertionIndex = lowerBound(leaveBehindItems, item); + setState(() { + leaveBehindItems.insert(insertionIndex, item); + }); + } + + Widget buildItem(LeaveBehindItem item) { + final ThemeData theme = Theme.of(context); + return new Dismissable( + key: new ObjectKey(item), + direction: _dismissDirection, + onDismissed: (DismissDirection direction) { + setState(() { + leaveBehindItems.remove(item); + }); + final String action = (direction == DismissDirection.endToStart) ? 'archived' : 'deleted'; + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text('You $action item ${item.index}'), + action: new SnackBarAction( + label: 'UNDO', + onPressed: () { handleUndo(item); } + ) + )); + }, + background: new Container( + decoration: new BoxDecoration(backgroundColor: theme.primaryColor), + child: new ListItem( + leading: new Icon(icon: Icons.delete, color: Colors.white, size: 36.0) + ) + ), + secondaryBackground: new Container( + decoration: new BoxDecoration(backgroundColor: theme.primaryColor), + child: new ListItem( + trailing: new Icon(icon: Icons.archive, color: Colors.white, size: 36.0) + ) + ), + child: new Container( + decoration: new BoxDecoration( + backgroundColor: theme.canvasColor, + border: new Border(bottom: new BorderSide(color: theme.dividerColor)) + ), + child: new ListItem( + title: new Text(item.name), + subtitle: new Text('${item.subject}\n${item.body}'), + isThreeLine: true + ) + ) + ); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar( + title: new Text('Swipe items to dismiss'), + actions: [ + new PopupMenuButton( + onSelected: handleDemoAction, + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: LeaveBehindDemoAction.reset, + child: new Text('Reset the list') + ), + new PopupMenuDivider(), + new CheckedPopupMenuItem( + value: LeaveBehindDemoAction.horizontalSwipe, + checked: _dismissDirection == DismissDirection.horizontal, + child: new Text('Hoizontal swipe') + ), + new CheckedPopupMenuItem( + value: LeaveBehindDemoAction.leftSwipe, + checked: _dismissDirection == DismissDirection.endToStart, + child: new Text('Only swipe left') + ), + new CheckedPopupMenuItem( + value: LeaveBehindDemoAction.rightSwipe, + checked: _dismissDirection == DismissDirection.startToEnd, + child: new Text('Only swipe right') + ) + ] + ) + ] + ), + body: new Block( + padding: new EdgeInsets.all(4.0), + children: leaveBehindItems.map(buildItem).toList() + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/list_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/list_demo.dart new file mode 100644 index 0000000000..d6714c3a04 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/list_demo.dart @@ -0,0 +1,205 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +class ListDemo extends StatefulWidget { + ListDemo({ Key key }) : super(key: key); + + static const String routeName = '/list'; + + @override + ListDemoState createState() => new ListDemoState(); +} + +class ListDemoState extends State { + final GlobalKey scaffoldKey = new GlobalKey(); + + PersistentBottomSheetController _bottomSheet; + MaterialListType _itemType = MaterialListType.threeLine; + bool _dense = false; + bool _showAvatars = true; + bool _showIcons = false; + bool _showDividers = false; + bool _reverseSort = false; + List items = [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N' + ]; + + void changeItemType(MaterialListType type) { + setState(() { + _itemType = type; + }); + _bottomSheet?.setState(() { }); + } + + void showConfigurationSheet(BuildContext appContext) { + _bottomSheet = scaffoldKey.currentState.showBottomSheet((BuildContext bottomSheetContext) { + return new Container( + decoration: new BoxDecoration( + border: new Border(top: new BorderSide(color: Colors.black26)) + ), + child: new Block( + children: [ + new ListItem( + dense: true, + title: new Text('One-line'), + trailing: new Radio( + value: _showAvatars ? MaterialListType.oneLineWithAvatar : MaterialListType.oneLine, + groupValue: _itemType, + onChanged: changeItemType + ) + ), + new ListItem( + dense: true, + title: new Text('Two-line'), + trailing: new Radio( + value: MaterialListType.twoLine, + groupValue: _itemType, + onChanged: changeItemType + ) + ), + new ListItem( + dense: true, + title: new Text('Three-line'), + trailing: new Radio( + value: MaterialListType.threeLine, + groupValue: _itemType, + onChanged: changeItemType + ) + ), + new ListItem( + dense: true, + title: new Text('Show avatar'), + trailing: new Checkbox( + value: _showAvatars, + onChanged: (bool value) { + setState(() { + _showAvatars = value; + }); + _bottomSheet?.setState(() { }); + } + ) + ), + new ListItem( + dense: true, + title: new Text('Show icon'), + trailing: new Checkbox( + value: _showIcons, + onChanged: (bool value) { + setState(() { + _showIcons = value; + }); + _bottomSheet?.setState(() { }); + } + ) + ), + new ListItem( + dense: true, + title: new Text('Show dividers'), + trailing: new Checkbox( + value: _showDividers, + onChanged: (bool value) { + setState(() { + _showDividers = value; + }); + _bottomSheet?.setState(() { }); + } + ) + ), + new ListItem( + dense: true, + title: new Text('Dense layout'), + trailing: new Checkbox( + value: _dense, + onChanged: (bool value) { + setState(() { + _dense = value; + }); + _bottomSheet?.setState(() { }); + } + ) + ) + ] + ) + ); + }); + } + + Widget buildListItem(BuildContext context, String item) { + Widget secondary; + if (_itemType == MaterialListType.twoLine) { + secondary = new Text( + "Additional item information." + ); + } else if (_itemType == MaterialListType.threeLine) { + secondary = new Text( + "Even more additional list item information appears on line three." + ); + } + return new ListItem( + isThreeLine: _itemType == MaterialListType.threeLine, + dense: _dense, + leading: _showAvatars ? new CircleAvatar(child: new Text(item)) : null, + title: new Text('This item represents $item.'), + subtitle: secondary, + trailing: _showIcons ? new Icon(icon: Icons.info, color: Theme.of(context).disabledColor) : null + ); + } + + @override + Widget build(BuildContext context) { + final String layoutText = _dense ? " \u2013 Dense" : ""; + String itemTypeText; + switch(_itemType) { + case MaterialListType.oneLine: + case MaterialListType.oneLineWithAvatar: + itemTypeText = 'Single-line'; + break; + case MaterialListType.twoLine: + itemTypeText = 'Two-line'; + break; + case MaterialListType.threeLine: + itemTypeText = 'Three-line'; + break; + } + + Iterable listItems = items.map((String item) => buildListItem(context, item)); + if (_showDividers) + listItems = ListItem.divideItems(context: context, items: listItems); + + return new Scaffold( + key: scaffoldKey, + appBar: new AppBar( + title: new Text('Scrolling list\n$itemTypeText$layoutText'), + actions: [ + new IconButton( + icon: Icons.sort_by_alpha, + tooltip: 'Sort', + onPressed: () { + setState(() { + _reverseSort = !_reverseSort; + items.sort((String a, String b) => _reverseSort ? b.compareTo(a) : a.compareTo(b)); + }); + } + ), + new IconButton( + icon: Icons.more_vert, + tooltip: 'Show menu', + onPressed: () { showConfigurationSheet(context); } + ) + ] + ), + body: new OverscrollIndicator( + child: new Scrollbar( + child: new MaterialList( + type: _itemType, + padding: new EdgeInsets.symmetric(vertical: _dense ? 4.0 : 8.0), + children: listItems + ) + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/menu_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/menu_demo.dart new file mode 100644 index 0000000000..a5df0e16fd --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/menu_demo.dart @@ -0,0 +1,216 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +class MenuDemo extends StatefulWidget { + MenuDemo({ Key key }) : super(key: key); + + static const String routeName = '/menu'; + + @override + MenuDemoState createState() => new MenuDemoState(); +} + +class MenuDemoState extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); + + final String _simpleValue1 = 'Menu item value one'; + final String _simpleValue2 = 'Menu item value two'; + final String _simpleValue3 = 'Menu item value three'; + String _simpleValue; + + final String _checkedValue1 = 'One'; + final String _checkedValue2 = 'Two'; + final String _checkedValue3 = 'Free'; + final String _checkedValue4 = 'Four'; + List _checkedValues; + + @override + void initState() { + super.initState(); + _simpleValue = _simpleValue2; + _checkedValues = [_checkedValue3]; + } + + void showInSnackBar(String value) { + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text(value) + )); + } + + void showMenuSelection(String value) { + if ([_simpleValue1, _simpleValue2, _simpleValue3].contains(value)) + _simpleValue = value; + showInSnackBar('You selected: $value'); + } + + void showCheckedMenuSelections(String value) { + if (_checkedValues.contains(value)) + _checkedValues.remove(value); + else + _checkedValues.add(value); + + showInSnackBar('Checked $_checkedValues'); + } + + bool isChecked(String value) => _checkedValues.contains(value); + + @override + Widget build(BuildContext context) { + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar( + title: new Text('Menus'), + actions: [ + new PopupMenuButton( + onSelected: showMenuSelection, + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: 'Toolbar menu', + child: new Text('Toolbar menu') + ), + new PopupMenuItem( + value: 'Right here', + child: new Text('Right here') + ), + new PopupMenuItem( + value: 'Hooray!', + child: new Text('Hooray!') + ), + ] + ) + ] + ), + body: new Block( + padding: const EdgeInsets.symmetric(vertical: 8.0), + children: [ + // Pressing the PopupMenuButton on the right of this item shows + // a simple menu with one disabled item. Typically the contents + // of this "contextual menu" would reflect the app's state. + new ListItem( + title: new Text('An item with a context menu button'), + trailing: new PopupMenuButton( + padding: EdgeInsets.zero, + onSelected: showMenuSelection, + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: _simpleValue1, + child: new Text('Context menu item one') + ), + new PopupMenuItem( + enabled: false, + child: new Text('A disabled menu item') + ), + new PopupMenuItem( + value: _simpleValue3, + child: new Text('Context menu item three') + ), + ] + ) + ), + // Pressing the PopupMenuButton on the right of this item shows + // a menu whose items have text labels and icons and a divider + // That separates the first three items from the last one. + new ListItem( + title: new Text('An item with a sectioned menu'), + trailing: new PopupMenuButton( + padding: EdgeInsets.zero, + onSelected: showMenuSelection, + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: 'Preview', + child: new ListItem( + leading: new Icon(icon: Icons.visibility), + title: new Text('Preview') + ) + ), + new PopupMenuItem( + value: 'Share', + child: new ListItem( + leading: new Icon(icon: Icons.person_add), + title: new Text('Share') + ) + ), + new PopupMenuItem( + value: 'Get Link', + child: new ListItem( + leading: new Icon(icon: Icons.link), + title: new Text('Get link') + ) + ), + new PopupMenuDivider(), + new PopupMenuItem( + value: 'Remove', + child: new ListItem( + leading: new Icon(icon: Icons.delete), + title: new Text('Remove') + ) + ) + ] + ) + ), + // This entire list item is a PopupMenuButton. Tapping anywhere shows + // a menu whose current value is highlighted and aligned over the + // list item's center line. + new PopupMenuButton( + padding: EdgeInsets.zero, + initialValue: _simpleValue, + onSelected: showMenuSelection, + child: new ListItem( + title: new Text('An item with a simple menu'), + subtitle: new Text(_simpleValue) + ), + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: _simpleValue1, + child: new Text(_simpleValue1) + ), + new PopupMenuItem( + value: _simpleValue2, + child: new Text(_simpleValue2) + ), + new PopupMenuItem( + value: _simpleValue3, + child: new Text(_simpleValue3) + ) + ] + ), + // Pressing the PopupMenuButton on the right of this item shows a menu + // whose items have checked icons that reflect this app's state. + new ListItem( + title: new Text('An item with a checklist menu'), + trailing: new PopupMenuButton( + padding: EdgeInsets.zero, + onSelected: showCheckedMenuSelections, + itemBuilder: (BuildContext context) => >[ + new CheckedPopupMenuItem( + value: _checkedValue1, + checked: isChecked(_checkedValue1), + child: new Text(_checkedValue1) + ), + new CheckedPopupMenuItem( + value: _checkedValue2, + enabled: false, + checked: isChecked(_checkedValue2), + child: new Text(_checkedValue2) + ), + new CheckedPopupMenuItem( + value: _checkedValue3, + checked: isChecked(_checkedValue3), + child: new Text(_checkedValue3) + ), + new CheckedPopupMenuItem( + value: _checkedValue4, + checked: isChecked(_checkedValue4), + child: new Text(_checkedValue4) + ) + ] + ) + ) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/modal_bottom_sheet_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/modal_bottom_sheet_demo.dart new file mode 100644 index 0000000000..d404e50e7b --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/modal_bottom_sheet_demo.dart @@ -0,0 +1,39 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +final TextStyle _kTextStyle = new TextStyle( + color: Colors.indigo[400], + fontSize: 24.0 +); + +class ModalBottomSheetDemo extends StatelessWidget { + static const String routeName = '/modal-bottom-sheet'; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Modal bottom sheet')), + body: new Center( + child: new RaisedButton( + child: new Text('SHOW BOTTOM SHEET'), + onPressed: () { + showModalBottomSheet/**/(context: context, builder: (BuildContext context) { + return new Container( + child: new Padding( + padding: const EdgeInsets.all(32.0), + child: new Text('This is the modal bottom sheet. Click anywhere to dismiss.', + style: _kTextStyle, + textAlign: TextAlign.center + ) + ) + ); + }); + } + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/overscroll_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/overscroll_demo.dart new file mode 100644 index 0000000000..3ee1f02d1a --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/overscroll_demo.dart @@ -0,0 +1,95 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; + +enum IndicatorType { overscroll, refresh } + +class OverscrollDemo extends StatefulWidget { + OverscrollDemo({ Key key }) : super(key: key); + + static const String routeName = '/overscroll'; + + @override + OverscrollDemoState createState() => new OverscrollDemoState(); +} + +class OverscrollDemoState extends State { + static final List _items = [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N' + ]; + + IndicatorType _type = IndicatorType.refresh; + + Future refresh() { + Completer completer = new Completer(); + new Timer(new Duration(seconds: 3), () { completer.complete(null); }); + return completer.future; + } + + + @override + Widget build(BuildContext context) { + String indicatorTypeText; + switch(_type) { + case IndicatorType.overscroll: + indicatorTypeText = 'Over-scroll indicator'; + break; + case IndicatorType.refresh: + indicatorTypeText = 'Refresh indicator'; + break; + } + + Widget body = new MaterialList( + type: MaterialListType.threeLine, + padding: const EdgeInsets.all(8.0), + children: _items.map((String item) { + return new ListItem( + isThreeLine: true, + leading: new CircleAvatar(child: new Text(item)), + title: new Text('This item represents $item.'), + subtitle: new Text('Even more additional list item information appears on line three.') + ); + }) + ); + switch(_type) { + case IndicatorType.overscroll: + body = new OverscrollIndicator(child: body); + break; + case IndicatorType.refresh: + body = new RefreshIndicator(child: body, refresh: refresh); + break; + } + + return new Scaffold( + appBar: new AppBar( + title: new Text('$indicatorTypeText'), + actions: [ + new IconButton( + icon: Icons.refresh, + tooltip: 'Pull to refresh', + onPressed: () { + setState(() { + _type = IndicatorType.refresh; + }); + } + ), + new IconButton( + icon: Icons.play_for_work, + tooltip: 'Over-scroll indicator', + onPressed: () { + setState(() { + _type = IndicatorType.overscroll; + }); + } + ) + ] + ), + body: body + ); + } + +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/page_selector_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/page_selector_demo.dart new file mode 100644 index 0000000000..7b7f0b9d3e --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/page_selector_demo.dart @@ -0,0 +1,81 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class PageSelectorDemo extends StatelessWidget { + + static const String routeName = '/page-selector'; + + void _handleArrowButtonPress(BuildContext context, int delta) { + final TabBarSelectionState selection = TabBarSelection.of/**/(context); + if (!selection.valueIsChanging) + selection.value = selection.values[(selection.index + delta).clamp(0, selection.values.length - 1)]; + } + + @override + Widget build(BuildContext notUsed) { // Can't find the TabBarSelection from this context. + final List icons = [ + Icons.event, + Icons.home, + Icons.android, + Icons.alarm, + Icons.face, + Icons.language, + ]; + + return new Scaffold( + appBar: new AppBar(title: new Text('Page selector')), + body: new TabBarSelection( + values: icons, + child: new Builder( + builder: (BuildContext context) { + final Color color = Theme.of(context).accentColor; + return new Column( + children: [ + new Container( + margin: const EdgeInsets.only(top: 16.0), + child: new Row( + children: [ + new IconButton( + icon: Icons.arrow_back, + color: color, + onPressed: () { _handleArrowButtonPress(context, -1); }, + tooltip: 'Page back' + ), + new TabPageSelector(), + new IconButton( + icon: Icons.arrow_forward, + color: color, + onPressed: () { _handleArrowButtonPress(context, 1); }, + tooltip: 'Page forward' + ) + ], + mainAxisAlignment: MainAxisAlignment.spaceBetween + ) + ), + new Flexible( + child: new TabBarView( + children: icons.map((IconData icon) { + return new Container( + key: new ObjectKey(icon), + padding: const EdgeInsets.all(12.0), + child: new Card( + child: new Center( + child: new Icon(icon: icon, size: 128.0, color: color) + ) + ) + ); + }) + .toList() + ) + ) + ] + ); + } + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/persistent_bottom_sheet_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/persistent_bottom_sheet_demo.dart new file mode 100644 index 0000000000..cb3c3b05c5 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/persistent_bottom_sheet_demo.dart @@ -0,0 +1,73 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +final TextStyle _kTextStyle = new TextStyle( + color: Colors.indigo[400], + fontSize: 24.0 +); + +class PersistentBottomSheetDemo extends StatefulWidget { + static const String routeName = '/persistent-bottom-sheet'; + + @override + _PersistentBottomSheetDemoState createState() => new _PersistentBottomSheetDemoState(); +} + +class _PersistentBottomSheetDemoState extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); + + VoidCallback _showBottomSheetCallback; + + @override + void initState() { + super.initState(); + _showBottomSheetCallback = showBottomSheet; + } + + + void showBottomSheet() { + setState(() { // disable the button + _showBottomSheetCallback = null; + }); + _scaffoldKey.currentState.showBottomSheet/**/((BuildContext context) { + return new Container( + decoration: new BoxDecoration( + border: new Border(top: new BorderSide(color: Colors.black26)) + ), + child: new Padding( + padding: const EdgeInsets.all(32.0), + child: new Text('This is a Material persistent bottom sheet. Drag downwards to dismiss it.', + style: _kTextStyle, + textAlign: TextAlign.center + ) + ) + ); + }) + .closed.then((_) { + setState(() { // re-enable the button + _showBottomSheetCallback = showBottomSheet; + }); + }); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar(title: new Text('Persistent bottom sheet')), + floatingActionButton: new FloatingActionButton( + child: new Icon(icon: Icons.add), + backgroundColor: Colors.redAccent[200] + ), + body: new Center( + child: new RaisedButton( + onPressed: _showBottomSheetCallback, + child: new Text('SHOW BOTTOM SHEET') + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/progress_indicator_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/progress_indicator_demo.dart new file mode 100644 index 0000000000..8c4e363e65 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/progress_indicator_demo.dart @@ -0,0 +1,113 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class ProgressIndicatorDemo extends StatefulWidget { + static const String routeName = '/progress-indicator'; + + @override + _ProgressIndicatorDemoState createState() => new _ProgressIndicatorDemoState(); +} + +class _ProgressIndicatorDemoState extends State { + AnimationController _controller; + Animation _animation; + + @override + void initState() { + super.initState(); + _controller = new AnimationController( + duration: const Duration(milliseconds: 1500) + )..forward(); + + _animation = new CurvedAnimation( + parent: _controller, + curve: new Interval(0.0, 0.9, curve: Curves.ease), + reverseCurve: Curves.ease + )..addStatusListener((AnimationStatus status) { + if (status == AnimationStatus.dismissed) + _controller.forward(); + else if (status == AnimationStatus.completed) + _controller.reverse(); + }); + } + + @override + void dispose() { + _controller.stop(); + super.dispose(); + } + + void _handleTap() { + setState(() { + // valueAnimation.isAnimating is part of our build state + if (_controller.isAnimating) { + _controller.stop(); + } else { + switch (_controller.status) { + case AnimationStatus.dismissed: + case AnimationStatus.forward: + _controller.forward(); + break; + case AnimationStatus.reverse: + case AnimationStatus.completed: + _controller.reverse(); + break; + } + } + }); + } + + Widget _buildIndicators(BuildContext context, Widget child) { + List indicators = [ + new SizedBox( + width: 200.0, + child: new LinearProgressIndicator() + ), + new LinearProgressIndicator(), + new LinearProgressIndicator(), + new LinearProgressIndicator(value: _animation.value), + new CircularProgressIndicator(), + new SizedBox( + width: 20.0, + height: 20.0, + child: new CircularProgressIndicator(value: _animation.value) + ), + new SizedBox( + width: 50.0, + height: 30.0, + child: new CircularProgressIndicator(value: _animation.value) + ), + new Text('${(_animation.value * 100.0).toStringAsFixed(1)}%${ _controller.isAnimating ? "" : " (paused)" }') + ]; + return new Column( + children: indicators + .map((Widget c) => new Container(child: c, margin: const EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0))) + .toList(), + mainAxisAlignment: MainAxisAlignment.center + ); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Progress indicators')), + body: new DefaultTextStyle( + style: Theme.of(context).textTheme.title, + child: new GestureDetector( + onTap: _handleTap, + behavior: HitTestBehavior.opaque, + child: new Container( + padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 8.0), + child: new AnimatedBuilder( + animation: _animation, + builder: _buildIndicators + ) + ) + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/scrollable_tabs_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/scrollable_tabs_demo.dart new file mode 100644 index 0000000000..2f00f0e3e6 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/scrollable_tabs_demo.dart @@ -0,0 +1,111 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +enum TabsDemoStyle { + iconsAndText, + iconsOnly, + textOnly +} + +class ScrollableTabsDemo extends StatefulWidget { + static const String routeName = '/scrollable-tabs'; + + @override + ScrollableTabsDemoState createState() => new ScrollableTabsDemoState(); +} + +class ScrollableTabsDemoState extends State { + final List icons = [ + Icons.event, + Icons.home, + Icons.android, + Icons.alarm, + Icons.face, + Icons.language, + ]; + + final Map labels = { + Icons.event: 'EVENT', + Icons.home: 'HOME', + Icons.android: 'ANDROID', + Icons.alarm: 'ALARM', + Icons.face: 'FACE', + Icons.language: 'LANGUAGE', + }; + + TabsDemoStyle _demoStyle = TabsDemoStyle.iconsAndText; + + void changeDemoStyle(TabsDemoStyle style) { + setState(() { + _demoStyle = style; + }); + } + + @override + Widget build(BuildContext context) { + final Color iconColor = Theme.of(context).accentColor; + return new TabBarSelection( + values: icons, + child: new Scaffold( + appBar: new AppBar( + title: new Text('Scrollable tabs'), + actions: [ + new PopupMenuButton( + onSelected: changeDemoStyle, + itemBuilder: (BuildContext context) => >[ + new PopupMenuItem( + value: TabsDemoStyle.iconsAndText, + child: new Text('Icons and text') + ), + new PopupMenuItem( + value: TabsDemoStyle.iconsOnly, + child: new Text('Icons only') + ), + new PopupMenuItem( + value: TabsDemoStyle.textOnly, + child: new Text('Text only') + ), + ] + ) + ], + tabBar: new TabBar( + isScrollable: true, + labels: new Map.fromIterable( + icons, + value: (IconData icon) { + switch(_demoStyle) { + case TabsDemoStyle.iconsAndText: + return new TabLabel(text: labels[icon], icon: icon); + case TabsDemoStyle.iconsOnly: + return new TabLabel(icon: icon); + case TabsDemoStyle.textOnly: + return new TabLabel(text: labels[icon]); + } + } + ) + ) + ), + body: new TabBarView( + children: icons.map((IconData icon) { + return new Container( + key: new ObjectKey(icon), + padding: const EdgeInsets.all(12.0), + child:new Card( + child: new Center( + child: new Icon( + icon: icon, + color: iconColor, + size: 128.0 + ) + ) + ) + ); + }).toList() + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/scrolling_techniques_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/scrolling_techniques_demo.dart new file mode 100644 index 0000000000..1a9b2ed434 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/scrolling_techniques_demo.dart @@ -0,0 +1,159 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +import 'flexible_space_demo.dart'; + +class _BarGraphic extends StatelessWidget { + _BarGraphic({ Key key, this.height, this.color, this.leftText, this.rightText: '' }) + : super(key: key) { + assert(height != null); + assert(color != null); + assert(leftText != null); + } + + final double height; + final Color color; + final String leftText; + final String rightText; + + @override + Widget build(BuildContext context) { + return new Container( + height: height, + width: 200.0, + padding: const EdgeInsets.symmetric(horizontal: 16.0), + decoration: new BoxDecoration(backgroundColor: color), + child: new DefaultTextStyle( + style: Theme.of(context).textTheme.body1.copyWith(color: Colors.white), + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + new Text(leftText), + new Text(rightText) + ] + ) + ) + ); + } +} + +class _StatusBarGraphic extends _BarGraphic { + _StatusBarGraphic() : super( + height: 24.0, + color: Colors.green[400], + leftText: 'Status Bar', + rightText: '24dp' + ); +} + +class _AppBarGraphic extends _BarGraphic { + _AppBarGraphic() : super( + height: 48.0, + color: Colors.blue[400], + leftText: 'Tool Bar', + rightText: '48dp' + ); +} + +class _TabBarGraphic extends _BarGraphic { + _TabBarGraphic() : super( + height: 48.0, + color: Colors.purple[400], + leftText: 'Tab Bar', + rightText: '56dp' + ); +} + +class _FlexibleSpaceGraphic extends _BarGraphic { + _FlexibleSpaceGraphic() : super( + height: 128.0, + color: Colors.pink[400], + leftText: 'Flexible Space' + ); +} + +class _TechniqueItem extends StatelessWidget { + _TechniqueItem({ this.titleText, this.barGraphics, this.builder }); + + final String titleText; + final List barGraphics; + final WidgetBuilder builder; + + void showDemo(BuildContext context) { + Navigator.push(context, new MaterialPageRoute(builder: builder)); + } + + @override + Widget build(BuildContext context) { + return new Card( + child: new InkWell( + onTap: () { showDemo(context); }, + child: new Padding( + padding: const EdgeInsets.all(16.0), + child: new Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children :[ + new Text(titleText), + new Column(children: barGraphics) + ] + ) + ) + ) + ); + } +} + +const String _introText = + "An AppBar is a combination of a ToolBar and a TabBar or a flexible space " + "Widget that is managed by the Scaffold. The Scaffold pads the ToolBar so that " + "it appears behind the device's status bar. When a flexible space Widget is " + "specified it is stacked on top of the ToolBar."; + +class ScrollingTechniquesDemo extends StatelessWidget { + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Scrolling techniques')), + body: new Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: new Block( + children: [ + new Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 32.0), + child: new Text(_introText, style: Theme.of(context).textTheme.caption) + ), + new _TechniqueItem( + builder: (BuildContext context) => new FlexibleSpaceDemo(), + titleText: 'Standard', + barGraphics: [ + new _StatusBarGraphic(), + new _AppBarGraphic() + ] + ), + new _TechniqueItem( + titleText: 'Tabs', + builder: (BuildContext context) => new FlexibleSpaceDemo(), + barGraphics: [ + new _StatusBarGraphic(), + new _AppBarGraphic(), + new _TabBarGraphic() + ] + ), + new _TechniqueItem( + titleText: 'Flexible', + builder: (BuildContext context) => new FlexibleSpaceDemo(), + barGraphics: [ + new _StatusBarGraphic(), + new _AppBarGraphic(), + new _FlexibleSpaceGraphic() + ] + ) + ] + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/selection_controls_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/selection_controls_demo.dart new file mode 100644 index 0000000000..29213d7f2b --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/selection_controls_demo.dart @@ -0,0 +1,180 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +import '../gallery/demo.dart'; + +const String _checkboxText = + "# Checkboxes\n" + "Checkboxes allow the user to select multiple options from a set."; + +const String _checkboxCode = 'selectioncontrols_checkbox'; + +const String _radioText = + "# Radio buttons\n" + "Radio buttons allow the user to select one option from a set. Use radio " + "buttons for exclusive selection if you think that the user needs to see " + "all available options side-by-side."; + +const String _radioCode = 'selectioncontrols_radio'; + +const String _switchText = + "# Switches\n" + "On/off switches toggle the state of a single settings option. The option " + "that the switch controls, as well as the state it’s in, should be made " + "clear from the corresponding inline label."; + +const String _switchCode = 'selectioncontrols_switch'; + +class SelectionControlsDemo extends StatefulWidget { + static const String routeName = '/selection-controls'; + + @override + _SelectionControlsDemoState createState() => new _SelectionControlsDemoState(); +} + +class _SelectionControlsDemoState extends State { + @override + Widget build(BuildContext context) { + List demos = [ + new ComponentDemoTabData( + tabName: "CHECKBOX", + description: _checkboxText, + widget: buildCheckbox(), + exampleCodeTag: _checkboxCode + ), + new ComponentDemoTabData( + tabName: "RADIO", + description: _radioText, + widget: buildRadio(), + exampleCodeTag: _radioCode + ), + new ComponentDemoTabData( + tabName: "SWITCH", + description: _switchText, + widget: buildSwitch(), + exampleCodeTag: _switchCode + ) + ]; + + return new TabbedComponentDemoScaffold( + title: 'Selection controls', + demos: demos + ); + } + + bool checkboxValueA = true; + bool checkboxValueB = false; + int radioValue = 0; + bool switchValue = false; + + void handleRadioValueChanged(int value) { + setState(() { + radioValue = value; + }); + } + + Widget buildCheckbox() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new Column( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new Row( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new Checkbox(value: checkboxValueA, onChanged: (bool value) { + setState(() { + checkboxValueA = value; + }); + }), + new Checkbox(value: checkboxValueB, onChanged: (bool value) { + setState(() { + checkboxValueB = value; + }); + }) + ] + ), + new Row( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + // Disabled checkboxes + new Checkbox(value: true), + new Checkbox(value: false) + ] + ) + ] + ) + ); + } + + Widget buildRadio() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new Column( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new Row( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new Radio( + value: 0, + groupValue: radioValue, + onChanged: handleRadioValueChanged + ), + new Radio( + value: 1, + groupValue: radioValue, + onChanged: handleRadioValueChanged + ), + new Radio( + value: 2, + groupValue: radioValue, + onChanged: handleRadioValueChanged + ) + ] + ), + // Disabled radio buttons + new Row( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new Radio( + value: 0, + groupValue: 0 + ), + new Radio( + value: 1, + groupValue: 0 + ), + new Radio( + value: 2, + groupValue: 0 + ) + ] + ) + ] + ) + ); + } + + Widget buildSwitch() { + return new Align( + alignment: new FractionalOffset(0.5, 0.4), + child: new Row( + mainAxisAlignment: MainAxisAlignment.collapse, + children: [ + new Switch(value: switchValue, onChanged: (bool value) { + setState(() { + switchValue = value; + }); + }), + // Disabled switches + new Switch(value: true), + new Switch(value: false) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/slider_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/slider_demo.dart new file mode 100644 index 0000000000..78d40eee05 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/slider_demo.dart @@ -0,0 +1,56 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class SliderDemo extends StatefulWidget { + static const String routeName = '/slider'; + + @override + _SliderDemoState createState() => new _SliderDemoState(); +} + +class _SliderDemoState extends State { + double _value = 25.0; + double _discreteValue = 20.0; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Sliders')), + body: new Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + new Center( + child: new Slider( + value: _value, + min: 0.0, + max: 100.0, + onChanged: (double value) { + setState(() { + _value = value; + }); + } + ) + ), + new Center(child: new Slider(value: _value / 100.0)), + new Center( + child: new Slider( + value: _discreteValue, + min: 0.0, + max: 100.0, + divisions: 5, + label: '${_discreteValue.round()}', + onChanged: (double value) { + setState(() { + _discreteValue = value; + }); + } + ) + ), + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/snack_bar_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/snack_bar_demo.dart new file mode 100644 index 0000000000..f0ed109bad --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/snack_bar_demo.dart @@ -0,0 +1,84 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +const String _text1 = + "Snackbars provide lightweight feedback about an operation by " + "showing a brief message at the bottom of the screen. Snackbars " + "can contain an action."; + +const String _text2 = + "Snackbars should contain a single line of text directly related " + "to the operation performed. They cannot contain icons."; + +const String _text3 = + "By default snackbars automatically disappear after a few seconds "; + +class SnackBarDemo extends StatefulWidget { + SnackBarDemo({ Key key }) : super(key: key); + + static const String routeName = '/snack-bar'; + + @override + _SnackBarDemoState createState() => new _SnackBarDemoState(); +} + +class _SnackBarDemoState extends State { + int _snackBarIndex = 1; + + Widget buildBody(BuildContext context) { + return new Padding( + padding: const EdgeInsets.all(24.0), + child: new Block( + children: [ + new Text(_text1), + new Text(_text2), + new Center( + child: new RaisedButton( + child: new Text('SHOW A SNACKBAR'), + onPressed: () { + final int thisSnackBarIndex = _snackBarIndex++; + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('This is snackbar #$thisSnackBarIndex.'), + action: new SnackBarAction( + label: 'ACTION', + onPressed: () { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('You pressed snackbar $thisSnackBarIndex\'s action.') + )); + } + ) + )); + } + ) + ), + new Text(_text3), + ] + .map((Widget child) { + return new Container( + margin: const EdgeInsets.symmetric(vertical: 12.0), + child: child + ); + }) + .toList() + ) + ); + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Snackbar') + ), + body: new Builder( + // Create an inner BuildContext so that the snackBar onPressed methods + // can refer to the Scaffold with Scaffold.of(). + builder: buildBody + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/tabs_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/tabs_demo.dart new file mode 100644 index 0000000000..ab26272b5d --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/tabs_demo.dart @@ -0,0 +1,82 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class _Page { + _Page({ this.label }); + + final GlobalKey> key = new GlobalKey>(); + final String label; +} + +final List<_Page> _pages = <_Page>[ + new _Page(label: 'ONE'), + new _Page(label: 'TWO'), + new _Page(label: 'FREE'), + new _Page(label: 'FOUR') +]; + +class TabsDemo extends StatefulWidget { + static const String routeName = '/tabs'; + + @override + TabsDemoState createState() => new TabsDemoState(); +} + +class TabsDemoState extends State { + _Page _selectedPage; + double _scrollOffset = 0.0; + + @override + void initState() { + super.initState(); + _selectedPage = _pages[0]; + } + + @override + Widget build(BuildContext context) { + final double statusBarHeight = MediaQuery.of(context).padding.top; + return new TabBarSelection<_Page>( + values: _pages, + onChanged: (_Page value) { + setState(() { + _selectedPage = value; + _selectedPage.key.currentState.scrollTo(_scrollOffset); + }); + }, + child: new Scaffold( + appBarBehavior: AppBarBehavior.under, + appBar: new AppBar( + title: new Text('Tabs and scrolling'), + tabBar: new TabBar<_Page>( + labels: new Map<_Page, TabLabel>.fromIterable(_pages, value: (_Page page) { + return new TabLabel(text: page.label); + }) + ) + ), + body: new TabBarView<_Page>( + children: _pages.map((_Page page) { + return new Block( + padding: new EdgeInsets.only(top: kTextTabBarHeight + kToolBarHeight + statusBarHeight), + scrollableKey: page.key, + onScroll: (double value) { _scrollOffset = value; }, + children: new List.generate(6, (int i) { + return new Container( + padding: const EdgeInsets.all(8.0), + height: 192.0, + child: new Card( + child: new Center( + child: new Text('Tab $page.label, item $i') + ) + ) + ); + }) + ); + }).toList() + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/tabs_fab_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/tabs_fab_demo.dart new file mode 100644 index 0000000000..6dbaec1bef --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/tabs_fab_demo.dart @@ -0,0 +1,118 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; + +class _Page { + _Page({ this.label, this.colors, this.icon }); + + final String label; + final Map colors; + final IconData icon; + + TabLabel get tabLabel => new TabLabel(text: label.toUpperCase()); + Color get labelColor => colors != null ? colors[300] : Colors.grey[300]; + bool get fabDefined => colors != null && icon != null; + Color get fabColor => colors[400]; + Icon get fabIcon => new Icon(icon: icon); + Key get fabKey => new ValueKey(fabColor); +} + +const String _explanatoryText = + "When the Scaffold's floating action button changes, the new button fades and " + "turns into view. In this demo, changing tabs can cause the app to be rebuilt " + "with a FloatingActionButton that the Scaffold distinguishes from the others " + "by its key."; + +class TabsFabDemo extends StatefulWidget { + static const String routeName = '/tabs-fab'; + + @override + _TabsFabDemoState createState() => new _TabsFabDemoState(); +} + +class _TabsFabDemoState extends State { + final GlobalKey scaffoldKey = new GlobalKey(); + final List<_Page> pages = <_Page>[ + new _Page(label: 'Blue', colors: Colors.indigo, icon: Icons.add), + new _Page(label: 'Eco', colors: Colors.green, icon: Icons.create), + new _Page(label: 'No'), + new _Page(label: 'Teal', colors: Colors.teal, icon: Icons.add), + new _Page(label: 'Red', colors: Colors.red, icon: Icons.create), + ]; + _Page selectedPage; + + @override + void initState() { + super.initState(); + selectedPage = pages[0]; + } + + void _handleTabSelection(_Page page) { + setState(() { + selectedPage = page; + }); + } + + void _showExplanatoryText() { + scaffoldKey.currentState.showBottomSheet((BuildContext context) { + return new Container( + decoration: new BoxDecoration( + border: new Border(top: new BorderSide(color: Theme.of(context).dividerColor)) + ), + child: new Padding( + padding: const EdgeInsets.all(32.0), + child: new Text(_explanatoryText, style: Theme.of(context).textTheme.subhead) + ) + ); + }); + } + + Widget buildTabView(_Page page) { + return new Builder( + builder: (BuildContext context) { + return new Container( + key: new ValueKey(page.label), + padding: const EdgeInsets.fromLTRB(48.0, 48.0, 48.0, 96.0), + child: new Card( + child: new Center( + child: new Text(page.label, + style: new TextStyle( + color: page.labelColor, + fontSize: 32.0 + ), + textAlign: TextAlign.center + ) + ) + ) + ); + } + ); + } + + @override + Widget build(BuildContext context) { + return new TabBarSelection<_Page>( + values: pages, + onChanged: _handleTabSelection, + child: new Scaffold( + key: scaffoldKey, + appBar: new AppBar( + title: new Text('FAB per tab'), + tabBar: new TabBar<_Page>( + labels: new Map<_Page, TabLabel>.fromIterable(pages, value: (_Page page) => page.tabLabel) + ) + ), + floatingActionButton: !selectedPage.fabDefined ? null : new FloatingActionButton( + key: selectedPage.fabKey, + tooltip: 'Show explanation', + backgroundColor: selectedPage.fabColor, + child: selectedPage.fabIcon, + onPressed: _showExplanatoryText + ), + body: new TabBarView<_Page>(children: pages.map(buildTabView).toList()) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/text_field_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/text_field_demo.dart new file mode 100644 index 0000000000..ec991537fd --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/text_field_demo.dart @@ -0,0 +1,120 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +class TextFieldDemo extends StatefulWidget { + TextFieldDemo({ Key key }) : super(key: key); + + static const String routeName = '/text-field'; + + @override + TextFieldDemoState createState() => new TextFieldDemoState(); +} + +class PersonData { + String name; + String phoneNumber; + String password; +} + +class TextFieldDemoState extends State { + final GlobalKey _scaffoldKey = new GlobalKey(); + + PersonData person = new PersonData(); + + void showInSnackBar(String value) { + _scaffoldKey.currentState.showSnackBar(new SnackBar( + content: new Text(value) + )); + } + + void _handleSubmitted() { + showInSnackBar('${person.name}\'s phone number is ${person.phoneNumber}'); + } + + String _validateName(String value) { + if (value.isEmpty) + return 'Name is required.'; + RegExp nameExp = new RegExp(r'^[A-za-z ]+$'); + if (!nameExp.hasMatch(value)) + return 'Please enter only alphabetical characters.'; + return null; + } + + String _validatePhoneNumber(String value) { + RegExp phoneExp = new RegExp(r'^\d\d\d-\d\d\d\-\d\d\d\d$'); + if (!phoneExp.hasMatch(value)) + return '###-###-#### - Please enter a valid phone number.'; + return null; + } + + String _validatePassword(String value) { + if (person.password == null || person.password.isEmpty) + return 'Please choose a password.'; + if (person.password != value) + return 'Passwords don\'t match'; + return null; + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + key: _scaffoldKey, + appBar: new AppBar( + title: new Text('Text fields') + ), + body: new Form( + onSubmitted: _handleSubmitted, + child: new Block( + padding: const EdgeInsets.all(8.0), + children: [ + new Input( + hintText: 'What do people call you?', + labelText: 'Name', + formField: new FormField( + // TODO(mpcomplete): replace with person#name= + setter: (String val) { person.name = val; }, + validator: _validateName + ) + ), + new Input( + hintText: 'Where can we reach you?', + labelText: 'Phone Number', + formField: new FormField( + setter: (String val) { person.phoneNumber = val; }, + validator: _validatePhoneNumber + ) + ), + new Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new Flexible( + child: new Input( + hintText: 'How do you log in?', + labelText: 'New Password', + hideText: true, + formField: new FormField( + setter: (String val) { person.password = val; } + ) + ) + ), + new Flexible( + child: new Input( + hintText: 'How do you log in?', + labelText: 'Re-type Password', + hideText: true, + formField: new FormField( + validator: _validatePassword + ) + ) + ) + ] + ) + ] + ) + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/time_picker_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/time_picker_demo.dart new file mode 100644 index 0000000000..06b048a9c6 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/time_picker_demo.dart @@ -0,0 +1,48 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class TimePickerDemo extends StatefulWidget { + static const String routeName = '/time-picker'; + + @override + _TimePickerDemoState createState() => new _TimePickerDemoState(); +} + +class _TimePickerDemoState extends State { + TimeOfDay _selectedTime = const TimeOfDay(hour: 7, minute: 28); + + Future _handleSelectTime() async { + TimeOfDay picked = await showTimePicker( + context: context, + initialTime: _selectedTime + ); + if (picked != _selectedTime) { + setState(() { + _selectedTime = picked; + }); + } + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Time picker')), + body: new Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new Text('$_selectedTime'), + new SizedBox(height: 20.0), + new RaisedButton( + onPressed: _handleSelectTime, + child: new Text('SELECT TIME') + ), + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/tooltip_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/tooltip_demo.dart new file mode 100644 index 0000000000..b9b41cba8a --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/tooltip_demo.dart @@ -0,0 +1,68 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +const String _introText = + "Tooltips are short identifying messages that briefly appear in response to " + "a long press. Tooltip messages are also used by services that make Flutter " + "apps accessible, like screen readers."; + +class TooltipDemo extends StatelessWidget { + + static const String routeName = '/tooltips'; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + return new Scaffold( + appBar: new AppBar( + title: new Text('Tooltips') + ), + body: new Builder( + builder: (BuildContext context) { + return new Block( + children: [ + new Text(_introText, style: theme.textTheme.subhead), + new Row( + children: [ + new Text('Long press the ', style: theme.textTheme.subhead), + new Tooltip( + message: 'call icon', + child: new Icon( + size: 18.0, + icon: Icons.call, + color: theme.primaryColor + ) + ), + new Text(' icon.', style: theme.textTheme.subhead) + ] + ), + new Center( + child: new IconButton( + size: 48.0, + icon: Icons.call, + color: theme.primaryColor, + tooltip: 'place a phone call', + onPressed: () { + Scaffold.of(context).showSnackBar(new SnackBar( + content: new Text('That was an ordinary tap.') + )); + } + ) + ) + ] + .map((Widget widget) { + return new Padding( + padding: const EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0), + child: widget + ); + }) + .toList() + ); + } + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/two_level_list_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/two_level_list_demo.dart new file mode 100644 index 0000000000..215fb06f42 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/two_level_list_demo.dart @@ -0,0 +1,32 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +class TwoLevelListDemo extends StatelessWidget { + static const String routeName = '/two-level-list'; + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar(title: new Text('Expand/collapse list control')), + body: new TwoLevelList( + type: MaterialListType.oneLine, + children: [ + new TwoLevelListItem(title: new Text('Top')), + new TwoLevelSublist( + title: new Text('Sublist'), + children: [ + new TwoLevelListItem(title: new Text('One')), + new TwoLevelListItem(title: new Text('Two')), + new TwoLevelListItem(title: new Text('Free')), + new TwoLevelListItem(title: new Text('Four')) + ] + ), + new TwoLevelListItem(title: new Text('Bottom')) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/typography_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/typography_demo.dart new file mode 100644 index 0000000000..5f3b7e0f94 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/typography_demo.dart @@ -0,0 +1,73 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +class TextStyleItem extends StatelessWidget { + TextStyleItem({ Key key, this.name, this.style, this.text }) : super(key: key) { + assert(name != null); + assert(style != null); + assert(text != null); + } + + final String name; + final TextStyle style; + final String text; + + @override + Widget build(BuildContext context) { + final ThemeData theme = Theme.of(context); + final TextStyle nameStyle = theme.textTheme.caption.copyWith(color: theme.textTheme.caption.color); + return new Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 16.0), + child: new Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + new SizedBox( + width: 64.0, + child: new Text(name, style: nameStyle) + ), + new Flexible( + child: new Text(text, style: style.copyWith(height: 1.0)) + ) + ] + ) + ); + } +} + +class TypographyDemo extends StatelessWidget { + static const String routeName = '/typography'; + + @override + Widget build(BuildContext context) { + final TextTheme textTheme = Theme.of(context).textTheme; + final List styleItems = [ + new TextStyleItem(name: 'Display 3', style: textTheme.display3, text: 'Regular 56sp'), + new TextStyleItem(name: 'Display 2', style: textTheme.display2, text: 'Regular 45sp'), + new TextStyleItem(name: 'Display 1', style: textTheme.display1, text: 'Regular 34sp'), + new TextStyleItem(name: 'Headline', style: textTheme.headline, text: 'Regular 24sp'), + new TextStyleItem(name: 'Title', style: textTheme.title, text: 'Medium 20sp'), + new TextStyleItem(name: 'Subheading', style: textTheme.subhead, text: 'Regular 16sp'), + new TextStyleItem(name: 'Body 2', style: textTheme.body2, text: 'Medium 14sp'), + new TextStyleItem(name: 'Body 1', style: textTheme.body1, text: 'Reguluar 14sp'), + new TextStyleItem(name: 'Caption', style: textTheme.caption, text: 'Regular 12sp'), + new TextStyleItem(name: 'Button', style: textTheme.button, text: 'MEDIUM (ALL CAPS) 14sp') + ]; + + if (MediaQuery.of(context).size.width > 500.0) { + styleItems.insert(0, new TextStyleItem( + name: 'Display 4', + style: textTheme.display4, + text: 'Light 112sp' + )); + } + + return new Scaffold( + appBar: new AppBar(title: new Text('Typography')), + body: new Block(children: styleItems) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/demo/weather_demo.dart b/examples/flutter_gallery/material_gallery/lib/demo/weather_demo.dart new file mode 100644 index 0000000000..defa7b1f45 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/demo/weather_demo.dart @@ -0,0 +1,559 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:ui' as ui show Image; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_sprites/flutter_sprites.dart'; + +ImageMap _images; +SpriteSheet _sprites; + +enum WeatherType { + sun, + rain, + snow +} + +class WeatherDemo extends StatefulWidget { + WeatherDemo({ Key key }) : super(key: key); + + static const String routeName = '/weather'; + + @override + _WeatherDemoState createState() => new _WeatherDemoState(); +} + +class _WeatherDemoState extends State { + + Future _loadAssets(AssetBundle bundle) async { + _images = new ImageMap(bundle); + await _images.load([ + 'packages/flutter_gallery_assets/clouds-0.png', + 'packages/flutter_gallery_assets/clouds-1.png', + 'packages/flutter_gallery_assets/ray.png', + 'packages/flutter_gallery_assets/sun.png', + 'packages/flutter_gallery_assets/weathersprites.png', + 'packages/flutter_gallery_assets/icon-sun.png', + 'packages/flutter_gallery_assets/icon-rain.png', + 'packages/flutter_gallery_assets/icon-snow.png' + ]); + + String json = await DefaultAssetBundle.of(context).loadString('packages/flutter_gallery_assets/weathersprites.json'); + _sprites = new SpriteSheet(_images['packages/flutter_gallery_assets/weathersprites.png'], json); + } + + @override + void initState() { + super.initState(); + + AssetBundle bundle = DefaultAssetBundle.of(context); + _loadAssets(bundle).then((_) { + setState(() { + assetsLoaded = true; + weatherWorld = new WeatherWorld(); + }); + }); + } + + bool assetsLoaded = false; + + WeatherWorld weatherWorld; + + @override + Widget build(BuildContext context) { + if (!assetsLoaded) { + return new Scaffold( + appBar: new AppBar( + title: new Text('Weather') + ), + body: new Container( + decoration: new BoxDecoration( + backgroundColor: const Color(0xff4aaafb) + ) + ) + ); + } + + return new Scaffold( + appBar: new AppBar( + title: new Text('Weather') + ), + body: new Material( + child: new Stack( + children: [ + new SpriteWidget(weatherWorld), + new Align( + alignment: new FractionalOffset(0.5, 0.8), + child: new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new WeatherButton( + onPressed: () { + setState(() { + weatherWorld.weatherType = WeatherType.sun; + }); + }, + selected: weatherWorld.weatherType == WeatherType.sun, + icon: "packages/flutter_gallery_assets/icon-sun.png" + ), + new WeatherButton( + onPressed: () { + setState(() { + weatherWorld.weatherType = WeatherType.rain; + }); + }, + selected: weatherWorld.weatherType == WeatherType.rain, + icon: "packages/flutter_gallery_assets/icon-rain.png" + ), + new WeatherButton( + onPressed: () { + setState(() { + weatherWorld.weatherType = WeatherType.snow; + }); + }, + selected: weatherWorld.weatherType == WeatherType.snow, + icon: "packages/flutter_gallery_assets/icon-snow.png" + ) + ] + ) + ) + ] + ) + ) + ); + } +} + +const double _kWeatherButtonSize = 56.0; +const double _kWeatherIconSize = 36.0; + +class WeatherButton extends StatelessWidget { + WeatherButton({ this.icon, this.selected, this.onPressed, Key key }) : super(key: key); + + final String icon; + final bool selected; + final VoidCallback onPressed; + + @override + Widget build(BuildContext context) { + Color color; + if (selected) + color = Theme.of(context).primaryColor; + else + color = const Color(0x33000000); + + return new Padding( + padding: const EdgeInsets.all(15.0), + child: new Material( + color: color, + type: MaterialType.circle, + elevation: 0, + child: new Container( + width: _kWeatherButtonSize, + height: _kWeatherButtonSize, + child: new InkWell( + onTap: onPressed, + child: new Center( + child: new AssetImage( + name: icon, + width: _kWeatherIconSize, + height: _kWeatherIconSize + ) + ) + ) + ) + ) + ); + } +} + +const List _kBackgroundColorsTop = const [ + const Color(0xff5ebbd5), + const Color(0xff0b2734), + const Color(0xffcbced7) +]; + +const List _kBackgroundColorsBottom = const [ + const Color(0xff4aaafb), + const Color(0xff4c5471), + const Color(0xffe0e3ec) +]; + +class WeatherWorld extends NodeWithSize { + WeatherWorld() : super(const Size(2048.0, 2048.0)) { + _background = new GradientNode( + this.size, + _kBackgroundColorsTop[0], + _kBackgroundColorsBottom[0] + ); + addChild(_background); + + _cloudsSharp = new CloudLayer( + image: _images['packages/flutter_gallery_assets/clouds-0.png'], + rotated: false, + dark: false, + loopTime: 20.0 + ); + addChild(_cloudsSharp); + + _cloudsDark = new CloudLayer( + image: _images['packages/flutter_gallery_assets/clouds-1.png'], + rotated: true, + dark: true, + loopTime: 40.0 + ); + addChild(_cloudsDark); + + _cloudsSoft = new CloudLayer( + image: _images['packages/flutter_gallery_assets/clouds-1.png'], + rotated: false, + dark: false, + loopTime: 60.0 + ); + addChild(_cloudsSoft); + + _sun = new Sun(); + _sun.position = const Point(1024.0, 1024.0); + _sun.scale = 1.5; + addChild(_sun); + + _rain = new Rain(); + addChild(_rain); + + _snow = new Snow(); + addChild(_snow); + } + + GradientNode _background; + CloudLayer _cloudsSharp; + CloudLayer _cloudsSoft; + CloudLayer _cloudsDark; + Sun _sun; + Rain _rain; + Snow _snow; + + WeatherType get weatherType => _weatherType; + + WeatherType _weatherType = WeatherType.sun; + + void set weatherType(WeatherType weatherType) { + if (weatherType == _weatherType) + return; + + _weatherType = weatherType; + + // Fade the background + _background.actions.stopAll(); + + _background.actions.run(new ActionTween( + (Color a) => _background.colorTop = a, + _background.colorTop, + _kBackgroundColorsTop[weatherType.index], + 1.0 + )); + + _background.actions.run(new ActionTween( + (Color a) => _background.colorBottom = a, + _background.colorBottom, + _kBackgroundColorsBottom[weatherType.index], + 1.0 + )); + + _cloudsDark.active = weatherType != WeatherType.sun; + _sun.active = weatherType == WeatherType.sun; + _rain.active = weatherType == WeatherType.rain; + _snow.active = weatherType == WeatherType.snow; + } + + @override + void spriteBoxPerformedLayout() { + _sun.position = spriteBox.visibleArea.topLeft + const Offset(350.0, 180.0); + } +} + +class GradientNode extends NodeWithSize { + GradientNode(Size size, this.colorTop, this.colorBottom) : super(size); + + Color colorTop; + Color colorBottom; + + @override + void paint(Canvas canvas) { + applyTransformForPivot(canvas); + + Rect rect = Point.origin & size; + Paint gradientPaint = new Paint()..shader = new LinearGradient( + begin: FractionalOffset.topLeft, + end: FractionalOffset.bottomLeft, + colors: [colorTop, colorBottom], + stops: [0.0, 1.0] + ).createShader(rect); + + canvas.drawRect(rect, gradientPaint); + } +} + +class CloudLayer extends Node { + CloudLayer({ ui.Image image, bool dark, bool rotated, double loopTime }) { + _sprites.add(_createSprite(image, dark, rotated)); + _sprites[0].position = const Point(1024.0, 1024.0); + addChild(_sprites[0]); + + _sprites.add(_createSprite(image, dark, rotated)); + _sprites[1].position = const Point(3072.0, 1024.0); + addChild(_sprites[1]); + + actions.run(new ActionRepeatForever( + new ActionTween( + (Point a) => position = a, + Point.origin, + const Point(-2048.0, 0.0), + loopTime) + )); + } + + List _sprites = []; + + Sprite _createSprite(ui.Image image, bool dark, bool rotated) { + Sprite sprite = new Sprite.fromImage(image); + + if (rotated) + sprite.scaleX = -1.0; + + if (dark) { + sprite.colorOverlay = const Color(0xff000000); + sprite.opacity = 0.0; + } + + return sprite; + } + + void set active(bool active) { + double opacity; + if (active) opacity = 1.0; + else opacity = 0.0; + + for (Sprite sprite in _sprites) { + sprite.actions.stopAll(); + sprite.actions.run(new ActionTween( + (double a) => sprite.opacity = a, + sprite.opacity, + opacity, + 1.0 + )); + } + } +} + +const double _kNumSunRays = 50.0; + +class Sun extends Node { + Sun() { + _sun = new Sprite.fromImage(_images['packages/flutter_gallery_assets/sun.png']); + _sun.scale = 4.0; + _sun.transferMode = TransferMode.plus; + addChild(_sun); + + _rays = []; + for (int i = 0; i < _kNumSunRays; i += 1) { + Ray ray = new Ray(); + addChild(ray); + _rays.add(ray); + } + } + + Sprite _sun; + List _rays; + + void set active(bool active) { + actions.stopAll(); + + double targetOpacity; + if (!active) targetOpacity = 0.0; + else targetOpacity = 1.0; + + actions.run( + new ActionTween( + (double a) => _sun.opacity = a, + _sun.opacity, + targetOpacity, + 2.0 + ) + ); + + if (active) { + for (Ray ray in _rays) { + actions.run(new ActionSequence([ + new ActionDelay(1.5), + new ActionTween( + (double a) => ray.opacity = a, + ray.opacity, + ray.maxOpacity, + 1.5 + ) + ])); + } + } else { + for (Ray ray in _rays) { + actions.run(new ActionTween( + (double a) => ray.opacity = a, + ray.opacity, + 0.0, + 0.2 + )); + } + } + } +} + +class Ray extends Sprite { + double _rotationSpeed; + double maxOpacity; + + Ray() : super.fromImage(_images['packages/flutter_gallery_assets/ray.png']) { + pivot = const Point(0.0, 0.5); + transferMode = TransferMode.plus; + rotation = randomDouble() * 360.0; + maxOpacity = randomDouble() * 0.2; + opacity = maxOpacity; + scaleX = 2.5 + randomDouble(); + scaleY = 0.3; + _rotationSpeed = randomSignedDouble() * 2.0; + + // Scale animation + double scaleTime = randomSignedDouble() * 2.0 + 4.0; + + actions.run(new ActionRepeatForever( + new ActionSequence([ + new ActionTween((double a) => scaleX = a, scaleX, scaleX * 0.5, scaleTime), + new ActionTween((double a) => scaleX = a, scaleX * 0.5, scaleX, scaleTime) + ]) + )); + } + + @override + void update(double dt) { + rotation += dt * _rotationSpeed; + } +} + +class Rain extends Node { + Rain() { + _addParticles(1.0); + _addParticles(1.5); + _addParticles(2.0); + } + + List _particles = []; + + void _addParticles(double distance) { + ParticleSystem particles = new ParticleSystem( + _sprites['raindrop.png'], + transferMode: TransferMode.srcATop, + posVar: const Point(1300.0, 0.0), + direction: 90.0, + directionVar: 0.0, + speed: 1000.0 / distance, + speedVar: 100.0 / distance, + startSize: 1.2 / distance, + startSizeVar: 0.2 / distance, + endSize: 1.2 / distance, + endSizeVar: 0.2 / distance, + life: 1.5 * distance, + lifeVar: 1.0 * distance + ); + particles.position = const Point(1024.0, -200.0); + particles.rotation = 10.0; + particles.opacity = 0.0; + + _particles.add(particles); + addChild(particles); + } + + void set active(bool active) { + actions.stopAll(); + for (ParticleSystem system in _particles) { + if (active) { + actions.run( + new ActionTween( + (double a) => system.opacity = a, + system.opacity, + 1.0, + 2.0 + )); + } else { + actions.run( + new ActionTween( + (double a) => system.opacity = a, + system.opacity, + 0.0, + 0.5 + )); + } + } + } +} + +class Snow extends Node { + Snow() { + _addParticles(_sprites['flake-0.png'], 1.0); + _addParticles(_sprites['flake-1.png'], 1.0); + _addParticles(_sprites['flake-2.png'], 1.0); + + _addParticles(_sprites['flake-3.png'], 1.5); + _addParticles(_sprites['flake-4.png'], 1.5); + _addParticles(_sprites['flake-5.png'], 1.5); + + _addParticles(_sprites['flake-6.png'], 2.0); + _addParticles(_sprites['flake-7.png'], 2.0); + _addParticles(_sprites['flake-8.png'], 2.0); + } + + List _particles = []; + + void _addParticles(Texture texture, double distance) { + ParticleSystem particles = new ParticleSystem( + texture, + transferMode: TransferMode.srcATop, + posVar: const Point(1300.0, 0.0), + direction: 90.0, + directionVar: 0.0, + speed: 150.0 / distance, + speedVar: 50.0 / distance, + startSize: 1.0 / distance, + startSizeVar: 0.3 / distance, + endSize: 1.2 / distance, + endSizeVar: 0.2 / distance, + life: 20.0 * distance, + lifeVar: 10.0 * distance, + emissionRate: 2.0, + startRotationVar: 360.0, + endRotationVar: 360.0, + radialAccelerationVar: 10.0 / distance, + tangentialAccelerationVar: 10.0 / distance + ); + particles.position = const Point(1024.0, -50.0); + particles.opacity = 0.0; + + _particles.add(particles); + addChild(particles); + } + + void set active(bool active) { + actions.stopAll(); + for (ParticleSystem system in _particles) { + if (active) { + actions.run( + new ActionTween((double a) => system.opacity = a, system.opacity, 1.0, 2.0 + )); + } else { + actions.run( + new ActionTween((double a) => system.opacity = a, system.opacity, 0.0, 0.5 + )); + } + } + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/app.dart b/examples/flutter_gallery/material_gallery/lib/gallery/app.dart new file mode 100644 index 0000000000..10b8758a9b --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/app.dart @@ -0,0 +1,85 @@ +// Copyright 2015 The Chromium Authors. 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/material.dart'; +import 'package:flutter/scheduler.dart' show timeDilation; + +import '../demo/all.dart'; +import 'home.dart'; + +final Map kRoutes = { + WeatherDemo.routeName: (BuildContext context) => new WeatherDemo(), + FitnessDemo.routeName: (BuildContext context) => new FitnessDemo(), + DrawingDemo.routeName: (BuildContext context) => new DrawingDemo(), + FlexibleSpaceDemo.routeName: (BuildContext context) => new FlexibleSpaceDemo(), + TabsFabDemo.routeName: (BuildContext context) => new TabsFabDemo(), + ButtonsDemo.routeName: (BuildContext context) => new ButtonsDemo(), + CardsDemo.routeName: (BuildContext context) => new CardsDemo(), + ChipDemo.routeName: (BuildContext context) => new ChipDemo(), + DatePickerDemo.routeName: (BuildContext context) => new DatePickerDemo(), + DataTableDemo.routeName: (BuildContext context) => new DataTableDemo(), + DialogDemo.routeName: (BuildContext context) => new DialogDemo(), + DropDownDemo.routeName: (BuildContext context) => new DropDownDemo(), + TwoLevelListDemo.routeName: (BuildContext context) => new TwoLevelListDemo(), + GridListDemo.routeName: (BuildContext context) => new GridListDemo(), + IconsDemo.routeName: (BuildContext context) => new IconsDemo(), + LeaveBehindDemo.routeName: (BuildContext context) => new LeaveBehindDemo(), + ListDemo.routeName: (BuildContext context) => new ListDemo(), + MenuDemo.routeName: (BuildContext context) => new MenuDemo(), + ModalBottomSheetDemo.routeName: (BuildContext context) => new ModalBottomSheetDemo(), + OverscrollDemo.routeName: (BuildContext context) => new OverscrollDemo(), + PageSelectorDemo.routeName: (BuildContext context) => new PageSelectorDemo(), + PersistentBottomSheetDemo.routeName: (BuildContext context) => new PersistentBottomSheetDemo(), + ProgressIndicatorDemo.routeName: (BuildContext context) => new ProgressIndicatorDemo(), + ScrollableTabsDemo.routeName: (BuildContext context) => new ScrollableTabsDemo(), + SelectionControlsDemo.routeName: (BuildContext context) => new SelectionControlsDemo(), + SliderDemo.routeName: (BuildContext context) => new SliderDemo(), + SnackBarDemo.routeName: (BuildContext context) => new SnackBarDemo(), + TabsDemo.routeName: (BuildContext context) => new TabsDemo(), + TextFieldDemo.routeName: (BuildContext context) => new TextFieldDemo(), + TimePickerDemo.routeName: (BuildContext context) => new TimePickerDemo(), + TooltipDemo.routeName: (BuildContext context) => new TooltipDemo(), + ColorsDemo.routeName: (BuildContext context) => new ColorsDemo(), + TypographyDemo.routeName: (BuildContext context) => new TypographyDemo(), +}; + +final ThemeData _kGalleryLightTheme = new ThemeData( + brightness: ThemeBrightness.light, + primarySwatch: Colors.purple +); + +final ThemeData _kGalleryDarkTheme = new ThemeData( + brightness: ThemeBrightness.dark, + primarySwatch: Colors.purple +); + +class GalleryApp extends StatefulWidget { + GalleryApp({ Key key }) : super(key: key); + + @override + GalleryAppState createState() => new GalleryAppState(); +} + +class GalleryAppState extends State { + bool _useLightTheme = true; + bool _showPerformanceOverlay = false; + + @override + Widget build(BuildContext context) { + return new MaterialApp( + title: 'Flutter Gallery', + theme: _useLightTheme ? _kGalleryLightTheme : _kGalleryDarkTheme, + showPerformanceOverlay: _showPerformanceOverlay, + routes: kRoutes, + home: new GalleryHome( + useLightTheme: _useLightTheme, + onThemeChanged: (bool value) { setState(() { _useLightTheme = value; }); }, + showPerformanceOverlay: _showPerformanceOverlay, + onShowPerformanceOverlayChanged: (bool value) { setState(() { _showPerformanceOverlay = value; }); }, + timeDilation: timeDilation, + onTimeDilationChanged: (double value) { setState(() { timeDilation = value; }); } + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/demo.dart b/examples/flutter_gallery/material_gallery/lib/gallery/demo.dart new file mode 100644 index 0000000000..5ce8e0fad5 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/demo.dart @@ -0,0 +1,278 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; + +import 'example_code_parser.dart'; +import 'syntax_highlighter.dart'; + +class SingleComponentDemoData { + SingleComponentDemoData({ + this.widget, + this.exampleCodeTag, + this.description, + this.onPressedDemo + }); + + final Widget widget; + final String exampleCodeTag; + final String description; + final VoidCallback onPressedDemo; +} + +class ComponentDemoTabData extends SingleComponentDemoData { + ComponentDemoTabData({ + Widget widget, + String exampleCodeTag, + String description, + VoidCallback onPressedDemo, + this.tabName + }) : super( + widget: widget, + exampleCodeTag: exampleCodeTag, + description: description, + onPressedDemo: onPressedDemo + ); + + final String tabName; + + static Map buildTabLabels(List demos) { + return new Map.fromIterable( + demos, + value: (ComponentDemoTabData demo) => new TabLabel(text: demo.tabName) + ); + } + + @override + bool operator==(Object other) { + if (other.runtimeType != runtimeType) + return false; + ComponentDemoTabData typedOther = other; + return typedOther.tabName == tabName && typedOther.description == description; + } + + @override + int get hashCode => hashValues(tabName.hashCode, description.hashCode); +} + +class TabbedComponentDemoScaffold extends StatelessWidget { + TabbedComponentDemoScaffold({ + this.title, + this.demos + }); + + final List demos; + final String title; + + @override + Widget build(BuildContext context) { + return new TabBarSelection( + values: demos, + child: new Scaffold( + appBar: new AppBar( + title: new Text(title), + tabBar: new TabBar( + isScrollable: true, + labels: ComponentDemoTabData.buildTabLabels(demos) + ) + ), + body: new TabbedComponentDemo(demos) + ) + ); + } +} + +class TabbedComponentDemo extends StatelessWidget { + TabbedComponentDemo(this.demos); + + final List demos; + + @override + Widget build(BuildContext context) { + return new TabBarView( + children: demos.map(buildTabView).toList() + ); + } + + Widget buildTabView(ComponentDemoTabData demo) { + return new SingleComponentDemo(demo); + } +} + +class SingleComponentDemo extends StatelessWidget { + SingleComponentDemo(this.demo); + + final SingleComponentDemoData demo; + + @override + Widget build(BuildContext context) { + return new Column( + children: [ + new Padding( + padding: new EdgeInsets.all(16.0), + child: new MarkdownBody(data: demo.description) + ), + new Flexible( + child: demo.widget + ), + new DemoBottomBar( + exampleCodeTag: demo.exampleCodeTag, + onPressedDemo: demo.onPressedDemo + ) + ] + ); + } +} + +class DemoBottomBar extends StatelessWidget { + DemoBottomBar({ this.exampleCodeTag, this.onPressedDemo }); + + final String exampleCodeTag; + final VoidCallback onPressedDemo; + + @override + Widget build(BuildContext context) { + VoidCallback onPressedCode; + if (exampleCodeTag != null) { + onPressedCode = () { + Navigator.push(context, new MaterialPageRoute( + builder: (BuildContext context) => new FullScreenCodeDialog(exampleCodeTag: exampleCodeTag) + )); + }; + } + + return new Column( + children: [ + new Divider( + height: 1.0 + ), + new Container( + height: 48.0, + child: new Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + new FlatButton( + child: new Row( + children: [ + new Padding( + padding: new EdgeInsets.only(right: 8.0), + child: new Icon(icon: Icons.code) + ), + new Text('VIEW CODE') + ] + ), + onPressed: onPressedCode + ), + new FlatButton( + child: new Row( + children: [ + new Padding( + padding: new EdgeInsets.only(right: 8.0), + child: new Icon(icon: Icons.star) + ), + new Text('LIVE DEMO') + ] + ), + onPressed: onPressedDemo + ) + ] + ) + ) + ] + ); + } +} + +class FormattedCode extends StatefulWidget { + FormattedCode(this.exampleCode); + + final String exampleCode; + + @override + _FormattedCodeState createState() => new _FormattedCodeState(); +} + +class _FormattedCodeState extends State { + @override + void initState() { + super.initState(); + _formatText(); + } + + TextSpan _formattedText; + + @override + Widget build(BuildContext context) { + return new RichText(text: _formattedText); + } + + @override + void didUpdateConfig(FormattedCode oldConfig) { + super.didUpdateConfig(oldConfig); + + if (oldConfig.exampleCode != config.exampleCode) + _formatText(); + } + + void _formatText() { + _formattedText = new TextSpan( + style: new TextStyle(fontFamily: 'monospace', fontSize: 10.0), + children: [new DartSyntaxHighlighter().format(config.exampleCode)] + ); + } +} + +class FullScreenCodeDialog extends StatefulWidget { + FullScreenCodeDialog({ this.exampleCodeTag }); + + final String exampleCodeTag; + + @override + FullScreenCodeDialogState createState() => new FullScreenCodeDialogState(); +} + +class FullScreenCodeDialogState extends State { + + String _exampleCode; + + @override + void initState() { + super.initState(); + + getExampleCode(config.exampleCodeTag, DefaultAssetBundle.of(context)).then((String code) { + setState(() { + _exampleCode = code; + }); + }); + } + + @override + Widget build(BuildContext context) { + Widget body; + if (_exampleCode == null) { + body = new Center( + child: new CircularProgressIndicator() + ); + } else { + body = new ScrollableViewport( + child: new Padding( + padding: new EdgeInsets.all(16.0), + child: new FormattedCode(_exampleCode) + ) + ); + } + + return new Scaffold( + appBar: new AppBar( + leading: new IconButton( + icon: Icons.clear, + onPressed: () { Navigator.pop(context); } + ), + title: new Text('Example code') + ), + body: body + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/drawer.dart b/examples/flutter_gallery/material_gallery/lib/gallery/drawer.dart new file mode 100644 index 0000000000..a5acc12035 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/drawer.dart @@ -0,0 +1,99 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; + +class GalleryDrawer extends StatelessWidget { + GalleryDrawer({ + Key key, + this.useLightTheme, + this.onThemeChanged, + this.timeDilation, + this.onTimeDilationChanged, + this.showPerformanceOverlay, + this.onShowPerformanceOverlayChanged + }) : super(key: key) { + assert(onThemeChanged != null); + assert(onTimeDilationChanged != null); + } + + final bool useLightTheme; + final ValueChanged onThemeChanged; + + final double timeDilation; + final ValueChanged onTimeDilationChanged; + + final bool showPerformanceOverlay; + final ValueChanged onShowPerformanceOverlayChanged; + + @override + Widget build(BuildContext context) { + return new Drawer( + child: new Block( + children: [ + new DrawerHeader(child: new Text('Flutter gallery')), + new DrawerItem( + icon: Icons.brightness_5, + onPressed: () { onThemeChanged(true); }, + selected: useLightTheme, + child: new Row( + children: [ + new Flexible(child: new Text('Light')), + new Radio( + value: true, + groupValue: useLightTheme, + onChanged: onThemeChanged + ) + ] + ) + ), + new DrawerItem( + icon: Icons.brightness_7, + onPressed: () { onThemeChanged(false); }, + selected: useLightTheme, + child: new Row( + children: [ + new Flexible(child: new Text('Dark')), + new Radio( + value: false, + groupValue: useLightTheme, + onChanged: onThemeChanged + ) + ] + ) + ), + new Divider(), + new DrawerItem( + icon: Icons.hourglass_empty, + selected: timeDilation != 1.0, + onPressed: () { onTimeDilationChanged(timeDilation != 1.0 ? 1.0 : 20.0); }, + child: new Row( + children: [ + new Flexible(child: new Text('Animate Slowly')), + new Checkbox( + value: timeDilation != 1.0, + onChanged: (bool value) { onTimeDilationChanged(value ? 20.0 : 1.0); } + ) + ] + ) + ), + new DrawerItem( + icon: Icons.assessment, + onPressed: () { onShowPerformanceOverlayChanged(!showPerformanceOverlay); }, + selected: showPerformanceOverlay, + child: new Row( + children: [ + new Flexible(child: new Text('Performance Overlay')), + new Checkbox( + value: showPerformanceOverlay, + onChanged: (bool value) { onShowPerformanceOverlayChanged(!showPerformanceOverlay); } + ) + ] + ) + ), + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/example_code.dart b/examples/flutter_gallery/material_gallery/lib/gallery/example_code.dart new file mode 100644 index 0000000000..f444059449 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/example_code.dart @@ -0,0 +1,230 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Note: This code is not runnable, it contains code snippets displayed in the +// gallery. + +import 'package:flutter/material.dart'; + +class ButtonsDemo { + void setState(VoidCallback callback) { } + BuildContext context; + + void buttons() { + +// START buttons_raised +// Create a raised button. +new RaisedButton( + child: new Text('BUTTON TITLE'), + onPressed: () { + // Perform some action + } +); + +// Create a disabled button. +// Buttons are disabled when onPressed isn't +// specified or is null. +new RaisedButton( + child: new Text('BUTTON TITLE') +); +// END + + +// START buttons_flat +// Create a flat button. +new FlatButton( + child: new Text('BUTTON TITLE'), + onPressed: () { + // Perform some action + } +); + +// Create a disabled button. +// Buttons are disabled when onPressed isn't +// specified or is null. +new FlatButton( + child: new Text('BUTTON TITLE') +); +// END + + +// START buttons_dropdown +// Member variable holding value. +String dropdownValue; + +// Drop down button with string values. +new DropDownButton( + value: dropdownValue, + onChanged: (String newValue) { + // null indicates the user didn't select a + // new value. + setState(() { + if (newValue != null) + dropdownValue = newValue; + }); + }, + items: ['One', 'Two', 'Free', 'Four'] + .map((String value) { + return new DropDownMenuItem( + value: value, + child: new Text(value)); + }) + .toList() +); +// END + + +// START buttons_icon +// Member variable holding toggle value. +bool value; + +// Toggleable icon button. +new IconButton( + icon: Icons.thumb_up, + onPressed: () { + setState(() => value = !value); + }, + color: value ? Theme.of(context).primaryColor : null +); +// END + + +// START buttons_action +// Floating action button in Scaffold. +new Scaffold( + appBar: new AppBar( + title: new Text('Demo') + ), + floatingActionButton: new FloatingActionButton( + child: new Icon(icon: Icons.add) + ) +); +// END + } +} + + +class SelectionControls { + void setState(VoidCallback callback) { } + + void selectionControls() { + +// START selectioncontrols_checkbox +// Member variable holding the checkbox's value. +bool checkboxValue = false; + +// Create a checkbox. +new Checkbox( + value: checkboxValue, + onChanged: (bool value) { + setState(() { + checkboxValue = value; + } + ); +}); + +// Create a disabled checkbox. +// Checkboxes are disabled when onChanged isn't +// specified or null. +new Checkbox(value: false); +// END + + +// START selectioncontrols_radio +// Member variable holding value. +int radioValue = 0; + +// Method setting value. +void handleRadioValueChanged(int value) { + setState(() { + radioValue = value; + }); +} + +// Creates a set of radio buttons. +new Row( + children: [ + new Radio( + value: 0, + groupValue: radioValue, + onChanged: handleRadioValueChanged + ), + new Radio( + value: 1, + groupValue: radioValue, + onChanged: handleRadioValueChanged + ), + new Radio( + value: 2, + groupValue: radioValue, + onChanged: handleRadioValueChanged + ) + ] +); + +// Creates a disabled radio button. +new Radio( + value: 0, + groupValue: 0 +); +// END + + +// START selectioncontrols_switch +// Member variable holding value. +bool switchValue = false; + +// Create a switch. +new Switch( + value: switchValue, + onChanged: (bool value) { + setState(() { + switchValue = value; + } + ); +}); + +// Create a disabled switch. +// Switches are disabled when onChanged isn't +// specified or null. +new Switch(value: false); +// END + } +} + + +class GridLists { + void gridlists() { +// START gridlists +// Creates a scrollable grid list with images +// loaded from the web. +new ScrollableGrid( + delegate: new FixedColumnCountGridDelegate( + columnCount: 3, + tileAspectRatio: 1.0, + padding: const EdgeInsets.all(4.0), + columnSpacing: 4.0, + rowSpacing: 4.0 + ), + children: [ + 'https://example.com/image-0.jpg', + 'https://example.com/image-1.jpg', + 'https://example.com/image-2.jpg', + '...', + 'https://example.com/image-n.jpg' + ].map((String url) { + return new GridTile( + footer: new GridTileBar( + title: new Text(url) + ), + child: new NetworkImage( + src: url, + fit: ImageFit.cover + ) + ); + }) +); +// END + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/example_code_parser.dart b/examples/flutter_gallery/material_gallery/lib/gallery/example_code_parser.dart new file mode 100644 index 0000000000..2cf0bff1c8 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/example_code_parser.dart @@ -0,0 +1,52 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; + +const String _kStartTag = '// START '; +const String _kEndTag = '// END'; + +Map _exampleCode; + +Future getExampleCode(String tag, AssetBundle bundle) async { + if (_exampleCode == null) + await _parseExampleCode(bundle); + return _exampleCode[tag]; +} + +Future _parseExampleCode(AssetBundle bundle) async { + final String code = await bundle.loadString('lib/gallery/example_code.dart'); + _exampleCode = {}; + + final List lines = code.split('\n'); + + List codeBlock; + String codeTag; + + for (String line in lines) { + if (codeBlock == null) { + // Outside a block. + if (line.startsWith(_kStartTag)) { + // Starting a new code block. + codeBlock = []; + codeTag = line.substring(_kStartTag.length); + } else { + // Just skipping the line. + } + } else { + // Inside a block. + if (line.startsWith(_kEndTag)) { + // Add the block. + _exampleCode[codeTag] = codeBlock.join('\n'); + codeBlock = null; + codeTag = null; + } else { + // Add to the current block + codeBlock.add(line); + } + } + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/header.dart b/examples/flutter_gallery/material_gallery/lib/gallery/header.dart new file mode 100644 index 0000000000..a69008f003 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/header.dart @@ -0,0 +1,198 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_sprites/flutter_sprites.dart'; + +class GalleryHeader extends StatefulWidget { + @override + _GalleryHeaderState createState() => new _GalleryHeaderState(); +} + +class _GalleryHeaderState extends State { + _FlutterHeaderNode _headerNode; + ImageMap _images; + + Future _loadAssets() async { + final AssetBundle bundle = DefaultAssetBundle.of(context); + _images = new ImageMap(bundle); + await _images.load([ + 'packages/flutter_gallery_assets/grain.png', + 'packages/flutter_gallery_assets/shadow.png', + ]); + } + + @override + void initState() { + super.initState(); + _loadAssets().then((_) { + setState(() { + _headerNode = new _FlutterHeaderNode(_images); + }); + }); + } + + @override + Widget build(BuildContext context) { + return _headerNode == null ? new Container() : new SpriteWidget(_headerNode); + } +} + +const Size _kCanvasSize = const Size(1024.0, 1024.0); +const Point _kCenterPoint = const Point(512.0, 512.0); + +class _FlutterHeaderNode extends NodeWithSize { + _FlutterHeaderNode(this._images) : super(_kCanvasSize) { + clippingLayer.opacity = 0.0; + clippingLayer.actions.run(new ActionTween((double a) => clippingLayer.opacity = a, 0.0, 1.0, 0.5)); + addChild(clippingLayer); + + clippingLayer.addChild(new _BackgroundBox()); + + paperAnimation = new _PaperAnimation(_images); + paperAnimation.position = _kCenterPoint; + clippingLayer.addChild(paperAnimation); + + final Sprite grain = new Sprite.fromImage(_images['packages/flutter_gallery_assets/grain.png']) + ..position = _kCenterPoint; + clippingLayer.addChild(grain); + + userInteractionEnabled = true; + } + + final ImageMap _images; + final Layer clippingLayer = new Layer(); + _PaperAnimation paperAnimation; + + @override + void spriteBoxPerformedLayout() { + clippingLayer.layerRect = spriteBox.visibleArea; + } +} + +final List<_PaperConfig> _kPaperConfigs = <_PaperConfig>[ + new _PaperConfig( + color: Colors.deepPurple[500], + startPosition: const Point(-300.0, -300.0), + startRotation: -10.0, + rotationSpeed: -1.0, + parallaxDepth: 0.0, + rect: new Rect.fromLTRB(-1024.0, -280.0, 1024.0, 280.0) + ), + new _PaperConfig( + color: Colors.purple[400], + startPosition: const Point(550.0, 0.0), + startRotation: 45.0, + rotationSpeed: 0.7, + parallaxDepth: 1.0, + rect: new Rect.fromLTRB(-512.0, -512.0, 512.0, 512.0) + ), + new _PaperConfig( + color: Colors.purple[600], + startPosition: const Point(550.0, 0.0), + startRotation: 55.0, + rotationSpeed: 0.9, + parallaxDepth: 2.0, + rect: new Rect.fromLTRB(-512.0, -512.0, 512.0, 512.0) + ), + new _PaperConfig( + color: Colors.purple[700], + startPosition: const Point(550.0, 0.0), + startRotation: 65.0, + rotationSpeed: 1.1, + parallaxDepth: 3.0, + rect: new Rect.fromLTRB(-512.0, -512.0, 512.0, 512.0) + ) +]; + +class _PaperAnimation extends Node { + _PaperAnimation(ImageMap images) { + for (_PaperConfig config in _kPaperConfigs) { + + final _PaperSheet sheet = new _PaperSheet(config); + final _PaperSheetShadow shadow = new _PaperSheetShadow(config, images); + + addChild(shadow); + addChild(sheet); + _sheets.add(sheet); + + shadow.constraints = [ + new ConstraintRotationToNodeRotation(sheet), + new ConstraintPositionToNode(sheet, offset: const Offset(0.0, 8.0)) + ]; + } + } + + final List<_PaperSheet> _sheets = <_PaperSheet>[]; +} + +class _PaperConfig { + _PaperConfig({ + this.color, + this.startPosition, + this.startRotation, + this.rotationSpeed, + this.parallaxDepth, + this.rect + }); + + final Color color; + final Point startPosition; + final double startRotation; + final double rotationSpeed; + final double parallaxDepth; + final Rect rect; +} + +class _PaperSheet extends Node { + _PaperSheet(this._config) { + _paperPaint.color = _config.color; + + position = _config.startPosition; + rotation = _config.startRotation; + } + + final _PaperConfig _config; + final Paint _paperPaint = new Paint(); + + @override + void paint(Canvas canvas) { + canvas.drawRect(_config.rect, _paperPaint); + } + + @override + void update(double dt) { + rotation += _config.rotationSpeed * dt; + } +} + +class _PaperSheetShadow extends Node { + _PaperSheetShadow(this._config, ImageMap images) { + NineSliceSprite shadow = new NineSliceSprite.fromImage( + images['packages/flutter_gallery_assets/shadow.png'], + new Size( + _config.rect.size.width + 32.0, + _config.rect.size.height + 32.0 + ), + const EdgeInsets.all(0.375) + ); + shadow.drawCenterPart = false; + shadow.opacity = 0.5; + addChild(shadow); + } + + final _PaperConfig _config; +} + +class _BackgroundBox extends Node { + final Paint _boxPaint = new Paint()..color = Colors.purple[500]; + + @override + void paint(Canvas canvas) { + canvas.drawRect(new Rect.fromLTWH(0.0, 0.0, _kCanvasSize.width, _kCanvasSize.height), _boxPaint); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/home.dart b/examples/flutter_gallery/material_gallery/lib/gallery/home.dart new file mode 100644 index 0000000000..d825a193b6 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/home.dart @@ -0,0 +1,127 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../demo/all.dart'; +import 'drawer.dart'; +import 'header.dart'; +import 'item.dart'; + +const double _kFlexibleSpaceMaxHeight = 256.0; + +class GalleryHome extends StatefulWidget { + GalleryHome({ + Key key, + this.useLightTheme, + this.onThemeChanged, + this.timeDilation, + this.onTimeDilationChanged, + this.showPerformanceOverlay, + this.onShowPerformanceOverlayChanged + }) : super(key: key) { + assert(onThemeChanged != null); + assert(onTimeDilationChanged != null); + assert(onShowPerformanceOverlayChanged != null); + } + + final bool useLightTheme; + final ValueChanged onThemeChanged; + + final double timeDilation; + final ValueChanged onTimeDilationChanged; + + final bool showPerformanceOverlay; + final ValueChanged onShowPerformanceOverlayChanged; + + @override + GalleryHomeState createState() => new GalleryHomeState(); +} + +class GalleryHomeState extends State { + final Key _homeKey = new ValueKey("Gallery Home"); + + @override + Widget build(BuildContext context) { + final double statusBarHight = (MediaQuery.of(context)?.padding ?? EdgeInsets.zero).top; + + return new Scaffold( + key: _homeKey, + drawer: new GalleryDrawer( + useLightTheme: config.useLightTheme, + onThemeChanged: config.onThemeChanged, + timeDilation: config.timeDilation, + onTimeDilationChanged: config.onTimeDilationChanged, + showPerformanceOverlay: config.showPerformanceOverlay, + onShowPerformanceOverlayChanged: config.onShowPerformanceOverlayChanged + ), + appBar: new AppBar( + expandedHeight: _kFlexibleSpaceMaxHeight, + flexibleSpace: new FlexibleSpaceBar( + title: new Text('Flutter gallery'), + background: new GalleryHeader() + ) + ), + appBarBehavior: AppBarBehavior.under, + body: new TwoLevelList( + padding: new EdgeInsets.only(top: _kFlexibleSpaceMaxHeight + statusBarHight), + type: MaterialListType.oneLine, + children: [ + new TwoLevelSublist( + leading: new Icon(icon: Icons.star), + title: new Text('Demos'), + children: [ + new GalleryItem(title: 'Weather', routeName: WeatherDemo.routeName), + new GalleryItem(title: 'Fitness', routeName: FitnessDemo.routeName), + new GalleryItem(title: 'Fancy lines', routeName: DrawingDemo.routeName), + new GalleryItem(title: 'Flexible space toolbar', routeName: FlexibleSpaceDemo.routeName), + new GalleryItem(title: 'Floating action button', routeName: TabsFabDemo.routeName), + ] + ), + new TwoLevelSublist( + leading: new Icon(icon: Icons.extension), + title: new Text('Components'), + children: [ + new GalleryItem(title: 'Buttons', routeName: ButtonsDemo.routeName), + new GalleryItem(title: 'Cards', routeName: CardsDemo.routeName), + new GalleryItem(title: 'Chips', routeName: ChipDemo.routeName), + new GalleryItem(title: 'Date picker', routeName: DatePickerDemo.routeName), + new GalleryItem(title: 'Data tables', routeName: DataTableDemo.routeName), + new GalleryItem(title: 'Dialog', routeName: DialogDemo.routeName), + new GalleryItem(title: 'Drop-down button', routeName: DropDownDemo.routeName), + new GalleryItem(title: 'Expand/collapse list control', routeName: TwoLevelListDemo.routeName), + new GalleryItem(title: 'Grid', routeName: GridListDemo.routeName), + new GalleryItem(title: 'Icons', routeName: IconsDemo.routeName), + new GalleryItem(title: 'Leave-behind list items', routeName: LeaveBehindDemo.routeName), + new GalleryItem(title: 'List', routeName: ListDemo.routeName), + new GalleryItem(title: 'Menus', routeName: MenuDemo.routeName), + new GalleryItem(title: 'Modal bottom sheet', routeName: ModalBottomSheetDemo.routeName), + new GalleryItem(title: 'Over-scroll', routeName: OverscrollDemo.routeName), + new GalleryItem(title: 'Page selector', routeName: PageSelectorDemo.routeName), + new GalleryItem(title: 'Persistent bottom sheet', routeName: PersistentBottomSheetDemo.routeName), + new GalleryItem(title: 'Progress indicators', routeName: ProgressIndicatorDemo.routeName), + new GalleryItem(title: 'Scrollable tabs', routeName: ScrollableTabsDemo.routeName), + new GalleryItem(title: 'Selection controls', routeName: SelectionControlsDemo.routeName), + new GalleryItem(title: 'Sliders', routeName: SliderDemo.routeName), + new GalleryItem(title: 'Snackbar', routeName: SnackBarDemo.routeName), + new GalleryItem(title: 'Tabs', routeName: TabsDemo.routeName), + new GalleryItem(title: 'Text fields', routeName: TextFieldDemo.routeName), + new GalleryItem(title: 'Time picker', routeName: TimePickerDemo.routeName), + new GalleryItem(title: 'Tooltips', routeName: TooltipDemo.routeName), + ] + ), + new TwoLevelSublist( + leading: new Icon(icon: Icons.color_lens), + title: new Text('Style'), + children: [ + new GalleryItem(title: 'Colors', routeName: ColorsDemo.routeName), + new GalleryItem(title: 'Typography', routeName: TypographyDemo.routeName), + ] + ) + ] + ) + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/item.dart b/examples/flutter_gallery/material_gallery/lib/gallery/item.dart new file mode 100644 index 0000000000..06b3326647 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/item.dart @@ -0,0 +1,36 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:developer'; + +import 'package:flutter/material.dart'; + +typedef Widget GalleryDemoBuilder(); + +class GalleryItem extends StatelessWidget { + GalleryItem({ this.title, this.icon, this.routeName }); + + final String title; + final IconData icon; + final String routeName; + + @override + Widget build(BuildContext context) { + Widget leading = icon == null ? new Container() : new Icon(icon: icon); + + return new TwoLevelListItem( + leading: leading, + title: new Text(title), + onTap: () { + if (routeName != null) { + Timeline.instantSync('Start Transition', arguments: { + 'from': '/', + 'to': routeName + }); + Navigator.pushNamed(context, routeName); + } + } + ); + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/gallery/syntax_highlighter.dart b/examples/flutter_gallery/material_gallery/lib/gallery/syntax_highlighter.dart new file mode 100644 index 0000000000..0a7190cbc3 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/gallery/syntax_highlighter.dart @@ -0,0 +1,345 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:string_scanner/string_scanner.dart'; + +class SyntaxHighlighterStyle { + SyntaxHighlighterStyle({ + this.baseStyle, + this.numberStyle, + this.commentStyle, + this.keywordStyle, + this.stringStyle, + this.punctuationStyle, + this.classStyle, + this.constantStyle + }); + + static SyntaxHighlighterStyle defaultStyle() { + return new SyntaxHighlighterStyle( + baseStyle: new TextStyle(color: const Color(0xff000000)), + numberStyle: new TextStyle(color: const Color(0xFF1565C0)), + commentStyle: new TextStyle(color: const Color(0xFF9E9E9E)), + keywordStyle: new TextStyle(color: const Color(0xFF9C27B0)), + stringStyle: new TextStyle(color: const Color(0xFF43A047)), + punctuationStyle: new TextStyle(color: const Color(0xff000000)), + classStyle: new TextStyle(color: const Color(0xFF512DA8)), + constantStyle: new TextStyle(color: const Color(0xFF795548)) + ); + } + + final TextStyle baseStyle; + final TextStyle numberStyle; + final TextStyle commentStyle; + final TextStyle keywordStyle; + final TextStyle stringStyle; + final TextStyle punctuationStyle; + final TextStyle classStyle; + final TextStyle constantStyle; +} + +abstract class SyntaxHighlighter { + TextSpan format(String src); +} + +class DartSyntaxHighlighter extends SyntaxHighlighter { + DartSyntaxHighlighter([this._style]) { + _spans = <_HighlightSpan>[]; + + if (_style == null) + _style = SyntaxHighlighterStyle.defaultStyle(); + } + + SyntaxHighlighterStyle _style; + + static const List _kKeywords = const [ + 'abstract', 'as', 'assert', 'async', 'await', 'break', 'case', 'catch', + 'class', 'const', 'continue', 'default', 'deferred', 'do', 'dynamic', 'else', + 'enum', 'export', 'external', 'extends', 'factory', 'false', 'final', + 'finally', 'for', 'get', 'if', 'implements', 'import', 'in', 'is', 'library', + 'new', 'null', 'operator', 'part', 'rethrow', 'return', 'set', 'static', + 'super', 'switch', 'sync', 'this', 'throw', 'true', 'try', 'typedef', 'var', + 'void', 'while', 'with', 'yield' + ]; + + static const List _kBuiltInTypes = const [ + 'int', 'double', 'num', 'bool' + ]; + + String _src; + StringScanner _scanner; + + List<_HighlightSpan> _spans; + + @override + TextSpan format(String src) { + _src = src; + _scanner = new StringScanner(_src); + + if (_generateSpans()) { + // Successfully parsed the code + List formattedText = []; + int currentPosition = 0; + + for (_HighlightSpan span in _spans) { + if (currentPosition != span.start) + formattedText.add(new TextSpan(text: _src.substring(currentPosition, span.start))); + + formattedText.add(new TextSpan(style: span.textStyle(_style), text: span.textForSpan(_src))); + + currentPosition = span.end; + } + + if (currentPosition != _src.length) + formattedText.add(new TextSpan(text: _src.substring(currentPosition, _src.length))); + + return new TextSpan(style: _style.baseStyle, children: formattedText); + } else { + // Parsing failed, return with only basic formatting + return new TextSpan(style:_style.baseStyle, text: src); + } + } + + bool _generateSpans() { + int lastLoopPosition = _scanner.position; + + while(!_scanner.isDone) { + // Skip White space + _scanner.scan(new RegExp(r"\s+")); + + // Block comments + if (_scanner.scan(new RegExp(r"/\*(.|\n)*\*/"))) { + _spans.add(new _HighlightSpan( + _HighlightType.comment, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Line comments + if (_scanner.scan("//")) { + int startComment = _scanner.lastMatch.start; + + bool eof = false; + int endComment; + if (_scanner.scan(new RegExp(r".*\n"))) { + endComment = _scanner.lastMatch.end - 1; + } else { + eof = true; + endComment = _src.length; + } + + _spans.add(new _HighlightSpan( + _HighlightType.comment, + startComment, + endComment + )); + + if (eof) + break; + + continue; + } + + // Raw r"String" + if (_scanner.scan(new RegExp(r'r".*"'))) { + _spans.add(new _HighlightSpan( + _HighlightType.string, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Raw r'String' + if (_scanner.scan(new RegExp(r"r'.*'"))) { + _spans.add(new _HighlightSpan( + _HighlightType.string, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Multiline """String""" + if (_scanner.scan(new RegExp(r'"""(?:[^"\\]|\\(.|\n))*"""'))) { + _spans.add(new _HighlightSpan( + _HighlightType.string, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Multiline '''String''' + if (_scanner.scan(new RegExp(r"'''(?:[^'\\]|\\(.|\n))*'''"))) { + _spans.add(new _HighlightSpan( + _HighlightType.string, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // "String" + if (_scanner.scan(new RegExp(r'"(?:[^"\\]|\\.)*"'))) { + _spans.add(new _HighlightSpan( + _HighlightType.string, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // 'String' + if (_scanner.scan(new RegExp(r"'(?:[^'\\]|\\.)*'"))) { + _spans.add(new _HighlightSpan( + _HighlightType.string, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Double + if (_scanner.scan(new RegExp(r"\d+\.\d+"))) { + _spans.add(new _HighlightSpan( + _HighlightType.number, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Integer + if (_scanner.scan(new RegExp(r"\d+"))) { + _spans.add(new _HighlightSpan( + _HighlightType.number, + _scanner.lastMatch.start, + _scanner.lastMatch.end) + ); + continue; + } + + // Punctuation + if (_scanner.scan(new RegExp(r"[\[\]{}().!=<>&\|\?\+\-\*/%\^~;:,]"))) { + _spans.add(new _HighlightSpan( + _HighlightType.punctuation, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Metadata + if (_scanner.scan(new RegExp(r"@\w+"))) { + _spans.add(new _HighlightSpan( + _HighlightType.keyword, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + continue; + } + + // Words + if (_scanner.scan(new RegExp(r"\w+"))) { + _HighlightType type; + + String word = _scanner.lastMatch[0]; + if (word.startsWith("_")) + word = word.substring(1); + + if (_kKeywords.contains(word)) + type = _HighlightType.keyword; + else if (_kBuiltInTypes.contains(word)) + type = _HighlightType.keyword; + else if (_firstLetterIsUpperCase(word)) + type = _HighlightType.klass; + else if (word.length >= 2 && word.startsWith("k") && _firstLetterIsUpperCase(word.substring(1))) + type = _HighlightType.constant; + + if (type != null) { + _spans.add(new _HighlightSpan( + type, + _scanner.lastMatch.start, + _scanner.lastMatch.end + )); + } + } + + // Check if this loop did anything + if (lastLoopPosition == _scanner.position) { + // Failed to parse this file, abort gracefully + return false; + } + lastLoopPosition = _scanner.position; + } + + _simplify(); + return true; + } + + void _simplify() { + for(int i = _spans.length - 2; i >= 0; i -= 1) { + if (_spans[i].type == _spans[i + 1].type && _spans[i].end == _spans[i + 1].start) { + _spans[i] = new _HighlightSpan( + _spans[i].type, + _spans[i].start, + _spans[i + 1].end + ); + _spans.removeAt(i + 1); + } + } + } + + bool _firstLetterIsUpperCase(String str) { + if (str.length > 0) { + String first = str.substring(0, 1); + return first == first.toUpperCase(); + } + return false; + } +} + +enum _HighlightType { + number, + comment, + keyword, + string, + punctuation, + klass, + constant +} + +class _HighlightSpan { + _HighlightSpan(this.type, this.start, this.end); + final _HighlightType type; + final int start; + final int end; + + String textForSpan(String src) { + return src.substring(start, end); + } + + TextStyle textStyle(SyntaxHighlighterStyle style) { + if (type == _HighlightType.number) + return style.numberStyle; + else if (type == _HighlightType.comment) + return style.commentStyle; + else if (type == _HighlightType.keyword) + return style.keywordStyle; + else if (type == _HighlightType.string) + return style.stringStyle; + else if (type == _HighlightType.punctuation) + return style.punctuationStyle; + else if (type == _HighlightType.klass) + return style.classStyle; + else if (type == _HighlightType.constant) + return style.constantStyle; + else + return style.baseStyle; + } +} diff --git a/examples/flutter_gallery/material_gallery/lib/main.dart b/examples/flutter_gallery/material_gallery/lib/main.dart new file mode 100644 index 0000000000..92f08efb71 --- /dev/null +++ b/examples/flutter_gallery/material_gallery/lib/main.dart @@ -0,0 +1,11 @@ +// Copyright 2015 The Chromium Authors. 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/widgets.dart'; + +import 'gallery/app.dart'; + +void main() { + runApp(new GalleryApp()); +} diff --git a/examples/material_gallery/pubspec.yaml b/examples/flutter_gallery/material_gallery/pubspec.yaml similarity index 95% rename from examples/material_gallery/pubspec.yaml rename to examples/flutter_gallery/material_gallery/pubspec.yaml index dc9e63e478..5756ac401e 100644 --- a/examples/material_gallery/pubspec.yaml +++ b/examples/flutter_gallery/material_gallery/pubspec.yaml @@ -1,4 +1,4 @@ -name: material_gallery +name: flutter_gallery dependencies: intl: '>=0.12.4+2 <0.13.0' collection: '>=1.4.0 <2.0.0' diff --git a/examples/material_gallery/test/example_code_parser_test.dart b/examples/flutter_gallery/material_gallery/test/example_code_parser_test.dart similarity index 90% rename from examples/material_gallery/test/example_code_parser_test.dart rename to examples/flutter_gallery/material_gallery/test/example_code_parser_test.dart index ffe5d25f5e..ca4b7575e1 100644 --- a/examples/material_gallery/test/example_code_parser_test.dart +++ b/examples/flutter_gallery/material_gallery/test/example_code_parser_test.dart @@ -5,12 +5,12 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:material_gallery/gallery/example_code_parser.dart'; +import 'package:flutter_gallery/gallery/example_code_parser.dart'; import 'package:mojo/core.dart' as core; import 'package:test/test.dart'; void main() { - test('Material Gallery example code parser test', () async { + test('Flutter gallery example code parser test', () async { TestAssetBundle bundle = new TestAssetBundle(); String codeSnippet0 = await getExampleCode('test_0', bundle); diff --git a/examples/material_gallery/test/smoke_test.dart b/examples/flutter_gallery/material_gallery/test/smoke_test.dart similarity index 86% rename from examples/material_gallery/test/smoke_test.dart rename to examples/flutter_gallery/material_gallery/test/smoke_test.dart index d2ab42ec2a..f8f398f08c 100644 --- a/examples/material_gallery/test/smoke_test.dart +++ b/examples/flutter_gallery/material_gallery/test/smoke_test.dart @@ -4,9 +4,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:material_gallery/gallery/app.dart' as material_gallery_app; -import 'package:material_gallery/gallery/item.dart' as material_gallery_item; -import 'package:material_gallery/main.dart' as material_gallery_main; +import 'package:flutter_gallery/gallery/app.dart' as flutter_gallery_app; +import 'package:flutter_gallery/gallery/item.dart' as flutter_gallery_item; +import 'package:flutter_gallery/main.dart' as flutter_gallery_main; import 'package:test/test.dart'; // Warning: the following strings must be kept in sync with GalleryHome. @@ -14,7 +14,7 @@ const List demoCategories = const ['Demos', 'Components', 'Style Finder findGalleryItemByRouteName(WidgetTester tester, String routeName) { return find.byWidgetPredicate((Widget widget) { - return widget is material_gallery_item.GalleryItem + return widget is flutter_gallery_item.GalleryItem && widget.routeName == routeName; }); } @@ -50,8 +50,8 @@ void smokeDemo(WidgetTester tester, String routeName) { } void main() { - testWidgets('Material Gallery app smoke test', (WidgetTester tester) { - material_gallery_main.main(); // builds the app and schedules a frame but doesn't trigger one + testWidgets('Flutter gallery app smoke test', (WidgetTester tester) { + flutter_gallery_main.main(); // builds the app and schedules a frame but doesn't trigger one tester.pump(); // see https://github.com/flutter/flutter/issues/1865 tester.pump(); // triggers a frame @@ -64,7 +64,7 @@ void main() { final List scrollDeltas = new List(); double previousY = tester.getTopRight(find.text(demoCategories[0])).y; - final List routeNames = material_gallery_app.kRoutes.keys.toList(); + final List routeNames = flutter_gallery_app.kRoutes.keys.toList(); for (String routeName in routeNames) { final double y = tester.getTopRight(findGalleryItemByRouteName(tester, routeName)).y; scrollDeltas.add(previousY - y); diff --git a/examples/material_gallery/test_driver/scroll_perf.dart b/examples/flutter_gallery/material_gallery/test_driver/scroll_perf.dart similarity index 84% rename from examples/material_gallery/test_driver/scroll_perf.dart rename to examples/flutter_gallery/material_gallery/test_driver/scroll_perf.dart index 1b0b033f24..c0010d05b2 100644 --- a/examples/material_gallery/test_driver/scroll_perf.dart +++ b/examples/flutter_gallery/material_gallery/test_driver/scroll_perf.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter_driver/driver_extension.dart'; -import 'package:material_gallery/main.dart' as app; +import 'package:flutter_gallery/main.dart' as app; void main() { enableFlutterDriverExtension(); diff --git a/examples/material_gallery/test_driver/scroll_perf_test.dart b/examples/flutter_gallery/material_gallery/test_driver/scroll_perf_test.dart similarity index 100% rename from examples/material_gallery/test_driver/scroll_perf_test.dart rename to examples/flutter_gallery/material_gallery/test_driver/scroll_perf_test.dart diff --git a/examples/material_gallery/test_driver/transitions_perf.dart b/examples/flutter_gallery/material_gallery/test_driver/transitions_perf.dart similarity index 84% rename from examples/material_gallery/test_driver/transitions_perf.dart rename to examples/flutter_gallery/material_gallery/test_driver/transitions_perf.dart index 1b0b033f24..c0010d05b2 100644 --- a/examples/material_gallery/test_driver/transitions_perf.dart +++ b/examples/flutter_gallery/material_gallery/test_driver/transitions_perf.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter_driver/driver_extension.dart'; -import 'package:material_gallery/main.dart' as app; +import 'package:flutter_gallery/main.dart' as app; void main() { enableFlutterDriverExtension(); diff --git a/examples/material_gallery/test_driver/transitions_perf_test.dart b/examples/flutter_gallery/material_gallery/test_driver/transitions_perf_test.dart similarity index 100% rename from examples/material_gallery/test_driver/transitions_perf_test.dart rename to examples/flutter_gallery/material_gallery/test_driver/transitions_perf_test.dart diff --git a/examples/flutter_gallery/pubspec.yaml b/examples/flutter_gallery/pubspec.yaml new file mode 100644 index 0000000000..5756ac401e --- /dev/null +++ b/examples/flutter_gallery/pubspec.yaml @@ -0,0 +1,20 @@ +name: flutter_gallery +dependencies: + intl: '>=0.12.4+2 <0.13.0' + collection: '>=1.4.0 <2.0.0' + string_scanner: '0.1.4+1' + + flutter: + path: ../../packages/flutter + flutter_sprites: + path: ../../packages/flutter_sprites + flutter_markdown: + path: ../../packages/flutter_markdown + flutter_gallery_assets: '0.0.16' + +dev_dependencies: + test: any # flutter_test provides the version constraints + flutter_test: + path: ../../packages/flutter_test + flutter_driver: + path: ../../packages/flutter_driver diff --git a/examples/flutter_gallery/test/example_code_parser_test.dart b/examples/flutter_gallery/test/example_code_parser_test.dart new file mode 100644 index 0000000000..ca4b7575e1 --- /dev/null +++ b/examples/flutter_gallery/test/example_code_parser_test.dart @@ -0,0 +1,53 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_gallery/gallery/example_code_parser.dart'; +import 'package:mojo/core.dart' as core; +import 'package:test/test.dart'; + +void main() { + test('Flutter gallery example code parser test', () async { + TestAssetBundle bundle = new TestAssetBundle(); + + String codeSnippet0 = await getExampleCode('test_0', bundle); + expect(codeSnippet0, 'test 0 0\ntest 0 1'); + + String codeSnippet1 = await getExampleCode('test_1', bundle); + expect(codeSnippet1, 'test 1 0\ntest 1 1'); + }); +} + +const String testCodeFile = """// A fake test file +// START test_0 +test 0 0 +test 0 1 +// END + +// Some comments +// START test_1 +test 1 0 +test 1 1 +// END +"""; + +class TestAssetBundle extends AssetBundle { + @override + ImageResource loadImage(String key) => null; + + @override + Future loadString(String key) { + if (key == 'lib/gallery/example_code.dart') + return new Future.value(testCodeFile); + return null; + } + + @override + Future load(String key) => null; + + @override + String toString() => '$runtimeType@$hashCode()'; +} diff --git a/examples/flutter_gallery/test/smoke_test.dart b/examples/flutter_gallery/test/smoke_test.dart new file mode 100644 index 0000000000..f8f398f08c --- /dev/null +++ b/examples/flutter_gallery/test/smoke_test.dart @@ -0,0 +1,98 @@ +// Copyright 2016 The Chromium Authors. 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/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_gallery/gallery/app.dart' as flutter_gallery_app; +import 'package:flutter_gallery/gallery/item.dart' as flutter_gallery_item; +import 'package:flutter_gallery/main.dart' as flutter_gallery_main; +import 'package:test/test.dart'; + +// Warning: the following strings must be kept in sync with GalleryHome. +const List demoCategories = const ['Demos', 'Components', 'Style']; + +Finder findGalleryItemByRouteName(WidgetTester tester, String routeName) { + return find.byWidgetPredicate((Widget widget) { + return widget is flutter_gallery_item.GalleryItem + && widget.routeName == routeName; + }); +} + +Finder byTooltip(WidgetTester tester, String message) { + return find.byWidgetPredicate((Widget widget) { + return widget is Tooltip && widget.message == message; + }); +} + +Finder findNavigationMenuButton(WidgetTester tester) => byTooltip(tester, 'Open navigation menu'); + +Finder findBackButton(WidgetTester tester) => byTooltip(tester, 'Back'); + +// Start a gallery demo and then go back. This function assumes that the +// we're starting on home route and that the submenu that contains the demo +// called 'name' is already open. +void smokeDemo(WidgetTester tester, String routeName) { + // Ensure that we're (likely to be) on the home page + final Finder menuItem = findGalleryItemByRouteName(tester, routeName); + expect(menuItem, findsOneWidget); + + tester.tap(menuItem); + tester.pump(); // Launch the demo. + tester.pump(const Duration(seconds: 1)); // Wait until the demo has opened. + + // Go back + Finder backButton = findBackButton(tester); + expect(backButton, findsOneWidget); + tester.tap(backButton); + tester.pump(); // Start the navigator pop "back" operation. + tester.pump(const Duration(seconds: 1)); // Wait until it has finished. +} + +void main() { + testWidgets('Flutter gallery app smoke test', (WidgetTester tester) { + flutter_gallery_main.main(); // builds the app and schedules a frame but doesn't trigger one + tester.pump(); // see https://github.com/flutter/flutter/issues/1865 + tester.pump(); // triggers a frame + + // Expand the demo category submenus. + for (String category in demoCategories.reversed) { + tester.tap(find.text(category)); + tester.pump(); + tester.pump(const Duration(seconds: 1)); // Wait until the menu has expanded. + } + + final List scrollDeltas = new List(); + double previousY = tester.getTopRight(find.text(demoCategories[0])).y; + final List routeNames = flutter_gallery_app.kRoutes.keys.toList(); + for (String routeName in routeNames) { + final double y = tester.getTopRight(findGalleryItemByRouteName(tester, routeName)).y; + scrollDeltas.add(previousY - y); + previousY = y; + } + + // Launch each demo and then scroll that item out of the way. + for (int i = 0; i < routeNames.length; i += 1) { + final String routeName = routeNames[i]; + smokeDemo(tester, routeName); + tester.scroll(findGalleryItemByRouteName(tester, routeName), new Offset(0.0, scrollDeltas[i])); + tester.pump(); + } + + Finder navigationMenuButton = findNavigationMenuButton(tester); + expect(navigationMenuButton, findsOneWidget); + tester.tap(navigationMenuButton); + tester.pump(); // Start opening drawer. + tester.pump(const Duration(seconds: 1)); // Wait until it's really opened. + + // switch theme + tester.tap(find.text('Dark')); + tester.pump(); + tester.pump(const Duration(seconds: 1)); // Wait until it's changed. + + // switch theme + tester.tap(find.text('Light')); + tester.pump(); + tester.pump(const Duration(seconds: 1)); // Wait until it's changed. + }, skip: true); +} diff --git a/examples/flutter_gallery/test_driver/scroll_perf.dart b/examples/flutter_gallery/test_driver/scroll_perf.dart new file mode 100644 index 0000000000..c0010d05b2 --- /dev/null +++ b/examples/flutter_gallery/test_driver/scroll_perf.dart @@ -0,0 +1,11 @@ +// Copyright 2016 The Chromium Authors. 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_driver/driver_extension.dart'; +import 'package:flutter_gallery/main.dart' as app; + +void main() { + enableFlutterDriverExtension(); + app.main(); +} diff --git a/examples/flutter_gallery/test_driver/scroll_perf_test.dart b/examples/flutter_gallery/test_driver/scroll_perf_test.dart new file mode 100644 index 0000000000..3e57813387 --- /dev/null +++ b/examples/flutter_gallery/test_driver/scroll_perf_test.dart @@ -0,0 +1,53 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +void main() { + group('scrolling performance test', () { + FlutterDriver driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) + driver.close(); + }); + + test('measure', () async { + Timeline timeline = await driver.traceAction(() async { + // Find the scrollable stock list + SerializableFinder stockList = find.byValueKey('Gallery List'); + expect(stockList, isNotNull); + + await driver.tap(find.text('Demos')); + await driver.tap(find.text('Components')); + await driver.tap(find.text('Style')); + + // TODO(eseidel): These are very artifical scrolls, we should use better + // https://github.com/flutter/flutter/issues/3316 + // Scroll down + for (int i = 0; i < 5; i++) { + await driver.scroll(stockList, 0.0, -300.0, new Duration(milliseconds: 300)); + await new Future.delayed(new Duration(milliseconds: 500)); + } + + // Scroll up + for (int i = 0; i < 5; i++) { + await driver.scroll(stockList, 0.0, 300.0, new Duration(milliseconds: 300)); + await new Future.delayed(new Duration(milliseconds: 500)); + } + }); + + new TimelineSummary.summarize(timeline) + ..writeSummaryToFile('home_scroll_perf', pretty: true) + ..writeTimelineToFile('home_scroll_perf', pretty: true); + }); + }); +} diff --git a/examples/flutter_gallery/test_driver/transitions_perf.dart b/examples/flutter_gallery/test_driver/transitions_perf.dart new file mode 100644 index 0000000000..c0010d05b2 --- /dev/null +++ b/examples/flutter_gallery/test_driver/transitions_perf.dart @@ -0,0 +1,11 @@ +// Copyright 2016 The Chromium Authors. 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_driver/driver_extension.dart'; +import 'package:flutter_gallery/main.dart' as app; + +void main() { + enableFlutterDriverExtension(); + app.main(); +} diff --git a/examples/flutter_gallery/test_driver/transitions_perf_test.dart b/examples/flutter_gallery/test_driver/transitions_perf_test.dart new file mode 100644 index 0000000000..96875db550 --- /dev/null +++ b/examples/flutter_gallery/test_driver/transitions_perf_test.dart @@ -0,0 +1,93 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +// Warning: the following strings must be kept in sync with GalleryHome. +const List demoCategories = const ['Demos', 'Components', 'Style']; + +const List demoNames = const [ + 'Weather', + 'Fitness', + 'Fancy lines', + 'Flexible space toolbar', + 'Floating action button', + 'Buttons', + 'Cards', + 'Chips', + 'Date picker', + 'Data tables', + 'Dialog', + 'Drop-down button', + 'Expand/collapse list control', + 'Grid', + 'Icons', + 'Leave-behind list items', + 'List', + 'Menus', + 'Modal bottom sheet', + 'Over-scroll', + 'Page selector', + 'Persistent bottom sheet', + 'Progress indicators', + 'Scrollable tabs', + 'Selection controls', + 'Sliders', + 'Snackbar', + 'Tabs', + 'Text fields', + 'Time picker', + 'Tooltips', + 'Colors', + 'Typography' +]; + +void main() { + group('flutter gallery transitions', () { + FlutterDriver driver; + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) + driver.close(); + }); + + test('all demos', () async { + Timeline timeline = await driver.traceAction(() async { + // Expand the demo category submenus. + for (String category in demoCategories.reversed) { + await driver.tap(find.text(category)); + await new Future.delayed(new Duration(milliseconds: 500)); + } + // Scroll each demo menu item into view, launch the demo and + // return to the demo menu 2x. + for(String demoName in demoNames) { + SerializableFinder menuItem = find.text(demoName); + await driver.scrollIntoView(menuItem); + await new Future.delayed(new Duration(milliseconds: 500)); + + for(int i = 0; i < 2; i += 1) { + await driver.tap(menuItem); // Launch the demo + await new Future.delayed(new Duration(milliseconds: 500)); + await driver.tap(find.byTooltip('Back')); + await new Future.delayed(new Duration(milliseconds: 1000)); + } + } + }, + categories: const [ + TracingCategory.dart, + TracingCategory.gc, + TracingCategory.compiler + ]); + new TimelineSummary.summarize(timeline) + ..writeSummaryToFile('transitions_perf', pretty: true) + ..writeTimelineToFile('transitions_perf', pretty: true); + }, timeout: new Timeout(new Duration(minutes: 15))); + }); +} diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart index 70c1b61059..7f8dc73686 100644 --- a/packages/flutter_tools/lib/src/commands/upgrade.dart +++ b/packages/flutter_tools/lib/src/commands/upgrade.dart @@ -70,7 +70,7 @@ class UpgradeCommand extends FlutterCommand { // rename {packages/flutter/doc => dev/docs}/styles.html (92%) // delete mode 100644 doc/index.html - // create mode 100644 examples/material_gallery/lib/gallery/demo.dart + // create mode 100644 examples/flutter_gallery/lib/gallery/demo.dart static final RegExp _gitChangedRegex = new RegExp(r' (rename|delete mode|create mode) .+'); // Public for testing. diff --git a/packages/flutter_tools/lib/src/flx.dart b/packages/flutter_tools/lib/src/flx.dart index 9f2fe39014..1144e3606f 100644 --- a/packages/flutter_tools/lib/src/flx.dart +++ b/packages/flutter_tools/lib/src/flx.dart @@ -57,7 +57,7 @@ class _Asset { bool get assetFileExists => assetFile.existsSync(); /// The delta between what the assetEntry is and the relativePath (e.g., - /// packages/material_gallery). + /// packages/flutter_gallery). String get symbolicPrefix { if (_assetEntry == null || _assetEntry == relativePath) return null; diff --git a/packages/flutter_tools/test/upgrade_test.dart b/packages/flutter_tools/test/upgrade_test.dart index 5eef614679..85c1b6e556 100644 --- a/packages/flutter_tools/test/upgrade_test.dart +++ b/packages/flutter_tools/test/upgrade_test.dart @@ -1,3 +1,4 @@ + // Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -10,12 +11,12 @@ void main() { bool _match(String line) => UpgradeCommand.matchesGitLine(line); test('regex match', () { - expect(_match(' .../material_gallery/lib/demo/buttons_demo.dart | 10 +--'), true); + expect(_match(' .../flutter_gallery/lib/demo/buttons_demo.dart | 10 +--'), true); expect(_match(' dev/benchmarks/complex_layout/lib/main.dart | 24 +-'), true); expect(_match(' rename {packages/flutter/doc => dev/docs}/styles.html (92%)'), true); expect(_match(' delete mode 100644 doc/index.html'), true); - expect(_match(' create mode 100644 examples/material_gallery/lib/gallery/demo.dart'), true); + expect(_match(' create mode 100644 examples/flutter_gallery/lib/gallery/demo.dart'), true); expect(_match('Fast-forward'), true); }); diff --git a/travis/test.sh b/travis/test.sh index 1622d1adb2..38a36972b1 100755 --- a/travis/test.sh +++ b/travis/test.sh @@ -17,7 +17,7 @@ flutter analyze --flutter-repo (cd dev/manual_tests; flutter test) (cd examples/hello_world; flutter test) (cd examples/layers; flutter test) -(cd examples/material_gallery; flutter test) +(cd examples/flutter_gallery; flutter test) (cd examples/stocks; flutter test) # generate and analyze our large sample app