diff --git a/examples/material_gallery/flutter.yaml b/examples/material_gallery/flutter.yaml index 113df7a724d..15a18730d25 100644 --- a/examples/material_gallery/flutter.yaml +++ b/examples/material_gallery/flutter.yaml @@ -37,3 +37,4 @@ assets: - packages/flutter_gallery_assets/landscape_9.jpg - packages/flutter_gallery_assets/landscape_10.jpg - packages/flutter_gallery_assets/landscape_11.jpg + - lib/gallery/example_code.dart diff --git a/examples/material_gallery/lib/demo/buttons_demo.dart b/examples/material_gallery/lib/demo/buttons_demo.dart index 9d238b8a612..283da0189c0 100644 --- a/examples/material_gallery/lib/demo/buttons_demo.dart +++ b/examples/material_gallery/lib/demo/buttons_demo.dart @@ -11,21 +11,7 @@ const String _raisedText = "Raised buttons add dimension to mostly flat layouts. They emphasize " "functions on busy or wide spaces."; -const String _raisedCode = -"""// 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') -);"""; +const String _raisedCode = 'buttons_raised'; const String _flatText = "# Flat buttons\n" @@ -33,21 +19,7 @@ const String _flatText = "but does not lift. Use flat buttons on toolbars, in dialogs and " "inline with padding"; -const String _flatCode = -"""// 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') -);"""; +const String _flatCode = 'buttons_flat'; const String _dropdownText = "# Dropdown buttons\n" @@ -55,46 +27,13 @@ const String _dropdownText = "small set of values. The button displays the current value and a down " "arrow."; -const String _dropdownCode = -"""// 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() -)"""; +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 = -"""// 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 -)"""; +const String _iconCode = 'buttons_icon'; const String _actionText = "# Floating action buttons\n" @@ -103,16 +42,7 @@ const String _actionText = "behaviors that include morphing, launching, and a transferring anchor " "point."; -const String _actionCode = -"""// Floating action button in Scaffold. -new Scaffold( - appBar: new AppBar( - title: new Text('Demo') - ), - floatingActionButton: new FloatingActionButton( - child: new Icon(icon: Icons.add) - ) -);"""; +const String _actionCode = 'buttons_action'; class ButtonsDemo extends StatefulWidget { @override @@ -127,32 +57,31 @@ class _ButtonsDemoState extends State { tabName: 'RAISED', description: _raisedText, widget: buildRaisedButton(), - exampleCode: _raisedCode + exampleCodeTag: _raisedCode ), new ComponentDemoTabData( tabName: 'FLAT', description: _flatText, widget: buildFlatButton(), - exampleCode: _flatCode + exampleCodeTag: _flatCode ), new ComponentDemoTabData( tabName: 'DROPDOWN', description: _dropdownText, widget: buildDropdownButton(), - exampleCode: - _dropdownCode + exampleCodeTag: _dropdownCode ), new ComponentDemoTabData( tabName: 'ICON', description: _iconText, widget: buildIconButton(), - exampleCode: _iconCode + exampleCodeTag: _iconCode ), new ComponentDemoTabData( tabName: 'ACTION', description: _actionText, widget: buildActionButton(), - exampleCode: _actionCode + exampleCodeTag: _actionCode ), ]; diff --git a/examples/material_gallery/lib/demo/grid_list_demo.dart b/examples/material_gallery/lib/demo/grid_list_demo.dart index e7eb481f2fb..3eaa95fd784 100644 --- a/examples/material_gallery/lib/demo/grid_list_demo.dart +++ b/examples/material_gallery/lib/demo/grid_list_demo.dart @@ -9,35 +9,7 @@ import 'package:flutter/widgets.dart'; import '../gallery/demo.dart'; -const String _kExampleCode = -"""// 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 - ) - ); - }) -);"""; +const String _kExampleCode = 'gridlists'; enum GridDemoTileStyle { imageOnly, @@ -301,7 +273,7 @@ class GridListDemoState extends State { ) ), new DemoBottomBar( - exampleCode: _kExampleCode + exampleCodeTag: _kExampleCode ) ] ) diff --git a/examples/material_gallery/lib/demo/selection_controls_demo.dart b/examples/material_gallery/lib/demo/selection_controls_demo.dart index 733c7b25219..92e440d6d11 100644 --- a/examples/material_gallery/lib/demo/selection_controls_demo.dart +++ b/examples/material_gallery/lib/demo/selection_controls_demo.dart @@ -10,24 +10,7 @@ const String _checkboxText = "# Checkboxes\n" "Checkboxes allow the user to select multiple options from a set."; -const String _checkboxCode = -"""// 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)"""; +const String _checkboxCode = 'selectioncontrols_checkbox'; const String _radioText = "# Radio buttons\n" @@ -35,43 +18,7 @@ const String _radioText = "buttons for exclusive selection if you think that the user needs to see " "all available options side-by-side."; -const String _radioCode = -"""// 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 -);"""; +const String _radioCode = 'selectioncontrols_radio'; const String _switchText = "# Switches\n" @@ -79,24 +26,7 @@ const String _switchText = "that the switch controls, as well as the state it’s in, should be made " "clear from the corresponding inline label."; -const String _switchCode = -"""// 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)"""; +const String _switchCode = 'selectioncontrols_switch'; class SelectionControlsDemo extends StatefulWidget { @override @@ -111,19 +41,19 @@ class _SelectionControlsDemoState extends State { tabName: "CHECKBOX", description: _checkboxText, widget: buildCheckbox(), - exampleCode: _checkboxCode + exampleCodeTag: _checkboxCode ), new ComponentDemoTabData( tabName: "RADIO", description: _radioText, widget: buildRadio(), - exampleCode: _radioCode + exampleCodeTag: _radioCode ), new ComponentDemoTabData( tabName: "SWITCH", description: _switchText, widget: buildSwitch(), - exampleCode: _switchCode + exampleCodeTag: _switchCode ) ]; diff --git a/examples/material_gallery/lib/gallery/demo.dart b/examples/material_gallery/lib/gallery/demo.dart index c9e561f0985..9e8ec0ab46b 100644 --- a/examples/material_gallery/lib/gallery/demo.dart +++ b/examples/material_gallery/lib/gallery/demo.dart @@ -5,18 +5,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import '../gallery/syntax_highlighter.dart'; +import 'syntax_highlighter.dart'; +import 'example_code_parser.dart'; class SingleComponentDemoData { SingleComponentDemoData({ this.widget, - this.exampleCode, + this.exampleCodeTag, this.description, this.onPressedDemo }); final Widget widget; - final String exampleCode; + final String exampleCodeTag; final String description; final VoidCallback onPressedDemo; } @@ -24,13 +25,13 @@ class SingleComponentDemoData { class ComponentDemoTabData extends SingleComponentDemoData { ComponentDemoTabData({ Widget widget, - String exampleCode, + String exampleCodeTag, String description, VoidCallback onPressedDemo, this.tabName }) : super( widget: widget, - exampleCode: exampleCode, + exampleCodeTag: exampleCodeTag, description: description, onPressedDemo: onPressedDemo ); @@ -117,7 +118,7 @@ class SingleComponentDemo extends StatelessWidget { child: demo.widget ), new DemoBottomBar( - exampleCode: demo.exampleCode, + exampleCodeTag: demo.exampleCodeTag, onPressedDemo: demo.onPressedDemo ) ] @@ -126,18 +127,18 @@ class SingleComponentDemo extends StatelessWidget { } class DemoBottomBar extends StatelessWidget { - DemoBottomBar({ this.exampleCode, this.onPressedDemo }); + DemoBottomBar({ this.exampleCodeTag, this.onPressedDemo }); - final String exampleCode; + final String exampleCodeTag; final VoidCallback onPressedDemo; @override Widget build(BuildContext context) { VoidCallback onPressedCode; - if (exampleCode != null) { + if (exampleCodeTag != null) { onPressedCode = () { Navigator.push(context, new MaterialPageRoute( - builder: (BuildContext context) => new FullScreenCodeDialog(code: exampleCode) + builder: (BuildContext context) => new FullScreenCodeDialog(exampleCodeTag: exampleCodeTag) )); }; } @@ -185,9 +186,9 @@ class DemoBottomBar extends StatelessWidget { } class FormattedCode extends StatefulWidget { - FormattedCode(this.code); + FormattedCode(this.exampleCode); - final String code; + final String exampleCode; @override _FormattedCodeState createState() => new _FormattedCodeState(); @@ -211,25 +212,58 @@ class _FormattedCodeState extends State { void didUpdateConfig(FormattedCode oldConfig) { super.didUpdateConfig(oldConfig); - if (oldConfig.code != config.code) + if (oldConfig.exampleCode != config.exampleCode) _formatText(); } void _formatText() { _formattedText = new TextSpan( style: new TextStyle(fontFamily: 'monospace', fontSize: 10.0), - children: [new DartSyntaxHighlighter().format(config.code)] + children: [new DartSyntaxHighlighter().format(config.exampleCode)] ); } } -class FullScreenCodeDialog extends StatelessWidget { - FullScreenCodeDialog({ this.code }); +class FullScreenCodeDialog extends StatefulWidget { + FullScreenCodeDialog({ this.exampleCodeTag }); - final String code; + 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( @@ -238,12 +272,7 @@ class FullScreenCodeDialog extends StatelessWidget { ), title: new Text('Example Code') ), - body: new ScrollableViewport( - child: new Padding( - padding: new EdgeInsets.all(16.0), - child: new FormattedCode(code) - ) - ) + body: body ); } } diff --git a/examples/material_gallery/lib/gallery/example_code.dart b/examples/material_gallery/lib/gallery/example_code.dart new file mode 100644 index 00000000000..f444059449b --- /dev/null +++ b/examples/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/material_gallery/lib/gallery/example_code_parser.dart b/examples/material_gallery/lib/gallery/example_code_parser.dart new file mode 100644 index 00000000000..61f1398e5eb --- /dev/null +++ b/examples/material_gallery/lib/gallery/example_code_parser.dart @@ -0,0 +1,54 @@ +// 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 { + print('getExampleCode tag: $tag bundle: $bundle'); + + 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/material_gallery/test/example_code_parser_test.dart b/examples/material_gallery/test/example_code_parser_test.dart new file mode 100644 index 00000000000..913d4e3812c --- /dev/null +++ b/examples/material_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:mojo/core.dart' as core; +import 'package:test/test.dart'; + +import '../lib/gallery/example_code_parser.dart'; + +void main() { + test('Material 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 Completer()..complete(testCodeFile)).future; + return null; + } + + @override + Future load(String key) => null; + + @override + String toString() => '$runtimeType@$hashCode()'; +} diff --git a/packages/flutter/lib/src/material/drop_down.dart b/packages/flutter/lib/src/material/drop_down.dart index a8f84e5d946..909cf0e9f34 100644 --- a/packages/flutter/lib/src/material/drop_down.dart +++ b/packages/flutter/lib/src/material/drop_down.dart @@ -241,6 +241,7 @@ class DropDownMenuItem extends StatelessWidget { child: new DefaultTextStyle( style: Theme.of(context).textTheme.subhead, child: new Baseline( + baselineType: TextBaseline.alphabetic, baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom, child: child )