Commit f7360126 authored by Viktor Lidholt's avatar Viktor Lidholt

Gallery code snippets now analyzed (#3391)

* Gallery code snippets now analyzed
parent c294014d
......@@ -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
......@@ -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<String>(
value: dropdownValue,
onChanged: (String newValue) {
// null indicates the user didn't select a
// new value.
setState(() {
if (newValue != null)
dropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map((String value) {
return new DropDownMenuItem<String>(
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<ButtonsDemo> {
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
),
];
......
......@@ -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: <String>[
'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<GridListDemo> {
)
),
new DemoBottomBar(
exampleCode: _kExampleCode
exampleCodeTag: _kExampleCode
)
]
)
......
......@@ -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: <Widget>[
new Radio<int>(
value: 0,
groupValue: radioValue,
onChanged: handleRadioValueChanged
),
new Radio<int>(
value: 1,
groupValue: radioValue,
onChanged: handleRadioValueChanged
),
new Radio<int>(
value: 2,
groupValue: radioValue,
onChanged: handleRadioValueChanged
)
]
);
// Creates a disabled radio button.
new Radio<int>(
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<SelectionControlsDemo> {
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
)
];
......
......@@ -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<FullScreenCodeDialog>(
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<FormattedCode> {
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: <TextSpan>[new DartSyntaxHighlighter().format(config.code)]
children: <TextSpan>[new DartSyntaxHighlighter().format(config.exampleCode)]
);
}
}
class FullScreenCodeDialog extends StatelessWidget {
FullScreenCodeDialog({ this.code });
class FullScreenCodeDialog extends StatefulWidget {
FullScreenCodeDialog({ this.exampleCodeTag });
final String exampleCodeTag;
@override
FullScreenCodeDialogState createState() => new FullScreenCodeDialogState();
}
class FullScreenCodeDialogState extends State<FullScreenCodeDialog> {
final String code;
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
);
}
}
// 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<String>(
value: dropdownValue,
onChanged: (String newValue) {
// null indicates the user didn't select a
// new value.
setState(() {
if (newValue != null)
dropdownValue = newValue;
});
},
items: <String>['One', 'Two', 'Free', 'Four']
.map((String value) {
return new DropDownMenuItem<String>(
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: <Widget>[
new Radio<int>(
value: 0,
groupValue: radioValue,
onChanged: handleRadioValueChanged
),
new Radio<int>(
value: 1,
groupValue: radioValue,
onChanged: handleRadioValueChanged
),
new Radio<int>(
value: 2,
groupValue: radioValue,
onChanged: handleRadioValueChanged
)
]
);
// Creates a disabled radio button.
new Radio<int>(
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: <String>[
'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
}
}
// 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<String, String> _exampleCode;
Future<String> getExampleCode(String tag, AssetBundle bundle) async {
print('getExampleCode tag: $tag bundle: $bundle');
if (_exampleCode == null)
await _parseExampleCode(bundle);
return _exampleCode[tag];
}
Future<Null> _parseExampleCode(AssetBundle bundle) async {
final String code = await bundle.loadString('lib/gallery/example_code.dart');
_exampleCode = <String, String>{};
final List<String> lines = code.split('\n');
List<String> codeBlock;
String codeTag;
for (String line in lines) {
if (codeBlock == null) {
// Outside a block.
if (line.startsWith(_kStartTag)) {
// Starting a new code block.
codeBlock = <String>[];
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);
}
}
}
}
// 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<String> loadString(String key) {if (key == 'lib/gallery/example_code.dart')
return (new Completer<String>()..complete(testCodeFile)).future;
return null;
}
@override
Future<core.MojoDataPipeConsumer> load(String key) => null;
@override
String toString() => '$runtimeType@$hashCode()';
}
......@@ -241,6 +241,7 @@ class DropDownMenuItem<T> extends StatelessWidget {
child: new DefaultTextStyle(
style: Theme.of(context).textTheme.subhead,
child: new Baseline(
baselineType: TextBaseline.alphabetic,
baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom,
child: child
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment