Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
2a140a77
Commit
2a140a77
authored
May 22, 2017
by
Hans Muller
Committed by
GitHub
May 22, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Sample Catalog page/screenshot production (#10212)
parent
ee345164
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
732 additions
and
317 deletions
+732
-317
.gitignore
examples/catalog/.gitignore
+3
-0
README.md
examples/catalog/README.md
+31
-0
sample_page.dart
examples/catalog/bin/sample_page.dart
+203
-0
sample_page.md.template
examples/catalog/bin/sample_page.md.template
+13
-0
screenshot.dart.template
examples/catalog/bin/screenshot.dart.template
+40
-0
screenshot_test.dart.template
examples/catalog/bin/screenshot_test.dart.template
+35
-0
animated_list.dart
examples/catalog/lib/animated_list.dart
+145
-125
app_bar_bottom.dart
examples/catalog/lib/app_bar_bottom.dart
+89
-76
basic_app_bar.dart
examples/catalog/lib/basic_app_bar.dart
+71
-58
expansion_tile_sample.dart
examples/catalog/lib/expansion_tile_sample.dart
+47
-16
tabbed_app_bar.dart
examples/catalog/lib/tabbed_app_bar.dart
+54
-42
animated_list_test.dart
examples/catalog/test/animated_list_test.dart
+0
-0
README.md
examples/catalog/test_driver/README.md
+1
-0
No files found.
examples/catalog/.gitignore
0 → 100644
View file @
2a140a77
.generated/
test_driver/screenshot.dart
test_driver/screenshot_test.dart
examples/catalog/README.md
0 → 100644
View file @
2a140a77
Samples Catalog
=======
A collection of sample apps that demonstrate how Flutter can be used.
Each sample app is contained in a single
`.dart`
file and they're all found in
the lib directory.
The apps are intended to be short and easily understood. Classes that represent
the sample's focus are at the top of the file, data and support classes follow.
Each sample app contains a comment (usually at the end) which provides some
standard documentation that also appears in the web view of the catalog.
See the "Generating..." section below.
Generating the web view of the catalog
---------
Markdown and a screenshot of each app are produced by
`bin/sample_page.dart`
and saved in the
`.generated`
directory. The markdown file contains
the text taken from the Sample Catalog comment found in the app's source
file, followed by the source code itself.
This sample_page.dart command line app must be run from the examples/catalog
directory. It relies on templates also found in the bin directory and it
generates and executes
`test_driver`
apps to collect the screenshots:
```
cd examples/catalog
dart bin/sample_page.dart
```
examples/catalog/bin/sample_page.dart
0 → 100644
View file @
2a140a77
// Copyright 2017 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.
// This application generates markdown pages and screenshots for each
// sample app. For more information see ../README.md.
import
'dart:io'
;
class
SampleError
extends
Error
{
SampleError
(
this
.
message
);
final
String
message
;
@override
String
toString
()
=>
message
;
}
// Sample apps are .dart files in the lib directory which contain a block
// comment that begins with a '/* Sample Catalog' line, and ends with a line
// that just contains '*/'. The following keywords may appear at the
// beginning of lines within the comment. A keyword's value is all of
// the following text up to the next keyword or the end of the comment,
// sans leading and trailing whitespace.
const
String
sampleCatalogKeywords
=
r'^Title:|^Summary:|^Description:|^Classes:|^Sample:|^See also:'
;
Directory
outputDirectory
;
Directory
sampleDirectory
;
Directory
testDirectory
;
Directory
driverDirectory
;
String
sampleTemplate
;
String
screenshotTemplate
;
String
screenshotDriverTemplate
;
void
logMessage
(
String
s
)
{
print
(
s
);
}
void
logError
(
String
s
)
{
print
(
s
);
}
File
inputFile
(
String
dir
,
String
name
)
{
return
new
File
(
dir
+
Platform
.
pathSeparator
+
name
);
}
File
outputFile
(
String
name
,
[
Directory
directory
])
{
return
new
File
((
directory
??
outputDirectory
).
path
+
Platform
.
pathSeparator
+
name
);
}
void
initialize
(
)
{
final
File
sampleTemplateFile
=
inputFile
(
'bin'
,
'sample_page.md.template'
);
final
File
screenshotTemplateFile
=
inputFile
(
'bin'
,
'screenshot.dart.template'
);
final
File
screenshotDriverTemplateFile
=
inputFile
(
'bin'
,
'screenshot_test.dart.template'
);
outputDirectory
=
new
Directory
(
'.generated'
);
sampleDirectory
=
new
Directory
(
'lib'
);
testDirectory
=
new
Directory
(
'test'
);
driverDirectory
=
new
Directory
(
'test_driver'
);
sampleTemplate
=
sampleTemplateFile
.
readAsStringSync
();
screenshotTemplate
=
screenshotTemplateFile
.
readAsStringSync
();
screenshotDriverTemplate
=
screenshotDriverTemplateFile
.
readAsStringSync
();
}
// Return a copy of template with each occurrence of @(foo) replaced
// by values[foo].
String
expandTemplate
(
String
template
,
Map
<
String
,
String
>
values
)
{
// Matches @(foo), match[1] == 'foo'
final
RegExp
tokenRE
=
new
RegExp
(
r'@\(([\w ]+)\)'
,
multiLine:
true
);
return
template
.
replaceAllMapped
(
tokenRE
,
(
Match
match
)
{
if
(
match
.
groupCount
!=
1
)
throw
new
SampleError
(
'bad template keyword
$match
[0]'
);
final
String
keyword
=
match
[
1
];
return
(
values
[
keyword
]
??
""
);
});
}
void
writeExpandedTemplate
(
File
output
,
String
template
,
Map
<
String
,
String
>
values
)
{
output
.
writeAsStringSync
(
expandTemplate
(
template
,
values
));
logMessage
(
'wrote
$output
'
);
}
class
SampleGenerator
{
SampleGenerator
(
this
.
sourceFile
);
final
File
sourceFile
;
String
sourceCode
;
Map
<
String
,
String
>
commentValues
;
// If sourceFile is lib/foo.dart then sourceName is foo. The sourceName
// is used to create derived filenames like foo.md or foo.png.
String
get
sourceName
{
// In /foo/bar/baz.dart, matches baz.dart, match[1] == 'baz'
final
RegExp
nameRE
=
new
RegExp
(
r'(\w+)\.dart$'
);
final
Match
nameMatch
=
nameRE
.
firstMatch
(
sourceFile
.
path
);
if
(
nameMatch
.
groupCount
!=
1
)
throw
new
SampleError
(
'bad source file name
${sourceFile.path}
'
);
return
nameMatch
[
1
];
}
// The name of the widget class that defines this sample app, like 'FooSample'.
String
get
sampleClass
=>
commentValues
[
"sample"
];
// The relative import path for this sample, like '../lib/foo.dart'.
String
get
importPath
=>
'..'
+
Platform
.
pathSeparator
+
sourceFile
.
path
;
// Return true if we're able to find the "Sample Catalog" comment in the
// sourceFile, and we're able to load its keyword/value pairs into
// the commentValues Map. The rest of the file's contents are saved
// in sourceCode.
bool
initialize
()
{
final
String
contents
=
sourceFile
.
readAsStringSync
();
final
RegExp
startRE
=
new
RegExp
(
r'^/\*\s+^Sample\s+Catalog'
,
multiLine:
true
);
final
RegExp
endRE
=
new
RegExp
(
r'^\*/'
,
multiLine:
true
);
final
Match
startMatch
=
startRE
.
firstMatch
(
contents
);
if
(
startMatch
==
null
)
return
false
;
final
int
startIndex
=
startMatch
.
end
;
final
Match
endMatch
=
endRE
.
firstMatch
(
contents
.
substring
(
startIndex
));
if
(
endMatch
==
null
)
return
false
;
final
String
comment
=
contents
.
substring
(
startIndex
,
startIndex
+
endMatch
.
start
);
sourceCode
=
contents
.
substring
(
0
,
startMatch
.
start
)
+
contents
.
substring
(
startIndex
+
endMatch
.
end
);
if
(
sourceCode
.
trim
().
isEmpty
)
throw
new
SampleError
(
'did not find any source code in
$sourceFile
'
);
final
RegExp
keywordsRE
=
new
RegExp
(
sampleCatalogKeywords
,
multiLine:
true
);
final
List
<
Match
>
keywordMatches
=
keywordsRE
.
allMatches
(
comment
).
toList
();
// TBD: fix error generation
if
(
keywordMatches
.
isEmpty
)
throw
new
SampleError
(
'did not find any keywords in the Sample Catalog comment in
$sourceFile
'
);
commentValues
=
<
String
,
String
>{};
for
(
int
i
=
0
;
i
<
keywordMatches
.
length
;
i
+=
1
)
{
final
String
keyword
=
comment
.
substring
(
keywordMatches
[
i
].
start
,
keywordMatches
[
i
].
end
-
1
);
final
String
value
=
comment
.
substring
(
keywordMatches
[
i
].
end
,
i
==
keywordMatches
.
length
-
1
?
null
:
keywordMatches
[
i
+
1
].
start
,
);
commentValues
[
keyword
.
toLowerCase
()]
=
value
.
trim
();
}
commentValues
[
'source'
]
=
sourceCode
.
trim
();
return
true
;
}
}
void
generate
(
)
{
initialize
();
final
List
<
SampleGenerator
>
samples
=
<
SampleGenerator
>[];
sampleDirectory
.
listSync
().
forEach
((
FileSystemEntity
entity
)
{
if
(
entity
is
File
&&
entity
.
path
.
endsWith
(
'.dart'
))
{
final
SampleGenerator
sample
=
new
SampleGenerator
(
entity
);
if
(
sample
.
initialize
())
{
// skip files that lack the Sample Catalog comment
writeExpandedTemplate
(
outputFile
(
sample
.
sourceName
+
'.md'
),
sampleTemplate
,
sample
.
commentValues
,
);
samples
.
add
(
sample
);
}
}
});
writeExpandedTemplate
(
outputFile
(
'screenshot.dart'
,
driverDirectory
),
screenshotTemplate
,
<
String
,
String
>{
'imports'
:
samples
.
map
((
SampleGenerator
page
)
{
return
"import '
${page.importPath}
' show
${page.sampleClass}
;
\n
"
;
}).
toList
().
join
(),
'widgets'
:
samples
.
map
((
SampleGenerator
sample
)
{
return
'new
${sample.sampleClass}
(),
\n
'
;
}).
toList
().
join
(),
},
);
writeExpandedTemplate
(
outputFile
(
'screenshot_test.dart'
,
driverDirectory
),
screenshotDriverTemplate
,
<
String
,
String
>{
'paths'
:
samples
.
map
((
SampleGenerator
sample
)
{
return
"'
${outputFile(sample.sourceName + '.png').path}
'"
;
}).
toList
().
join
(
',
\n
'
),
},
);
final
List
<
String
>
flutterDriveArgs
=
<
String
>[
'drive'
,
'test_driver/screenshot.dart'
];
logMessage
(
'Generating screenshots with: flutter
${flutterDriveArgs.join(" ")}
'
);
Process
.
runSync
(
'flutter'
,
flutterDriveArgs
);
}
void
main
(
List
<
String
>
args
)
{
try
{
generate
();
}
catch
(
error
)
{
logError
(
'Error: sample_page.dart failed:
$error
\n
'
'This sample_page.dart app expects to be run from the examples/catalog directory. '
'More information can be found in examples/catalog/README.md.'
);
exit
(
255
);
}
exit
(
0
);
}
examples/catalog/bin/sample_page.md.template
0 → 100644
View file @
2a140a77
@(title)
=============
@(summary)
@(description)
See also:
@(see also)
```
@(source)
```
examples/catalog/bin/screenshot.dart.template
0 → 100644
View file @
2a140a77
// This file was generated using bin/screenshot.dart.template and
// bin/sample_page.dart. For more information see README.md.
import 'package:flutter_driver/driver_extension.dart';
import 'package:flutter/material.dart';
@(imports)
class SampleScreenshots extends StatefulWidget {
@override
SampleScreenshotsState createState() => new SampleScreenshotsState();
}
class SampleScreenshotsState extends State<SampleScreenshots> {
final List<Widget> samples = <Widget>[
@(widgets)
];
int sampleIndex = 0;
@override
Widget build(BuildContext context) {
return new GestureDetector(
key: const ValueKey<String>('screenshotGestureDetector'),
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
sampleIndex += 1;
});
},
child: new IgnorePointer(
child: samples[sampleIndex % samples.length],
),
);
}
}
void main() {
enableFlutterDriverExtension();
runApp(new SampleScreenshots());
}
examples/catalog/bin/screenshot_test.dart.template
0 → 100644
View file @
2a140a77
// This file was generated using bin/screenshot_test.dart.template and
// bin/sample_page.dart. For more information see README.md.
import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('sample screenshots', () async {
FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
await driver?.close();
});
test('take sample screenshots', () async {
final List<String> paths = <String>[
@(paths)
];
for (String path in paths) {
final List<int> pixels = await driver.screenshot();
final File file = new File(path);
await file.writeAsBytes(pixels);
print('wrote $file');
await driver.tap(find.byValueKey('screenshotGestureDetector'));
await driver.waitUntilNoTransientCallbacks();
}
});
});
}
examples/catalog/lib/animated_list.dart
View file @
2a140a77
...
...
@@ -2,113 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// A sample app that demonstrates using an AnimatedList.
//
// Tap an item to select it, tap it again to unselect. Tap '+' to insert at the
// selected item, '-' to remove the selected item.
//
// This app includes a ListModel<E> class, a simple encapsulation of List<E>
// that keeps an AnimatedList in sync.
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
/// Displays its integer item as 'item N' on a Card whose color is based on
/// the item's value. The text is displayed in bright green if selected is true.
/// This widget's height is based on the animation parameter, it varies
/// from 0 to 128 as the animation varies from 0.0 to 1.0.
class
CardItem
extends
StatelessWidget
{
CardItem
({
Key
key
,
@required
this
.
animation
,
this
.
onTap
,
@required
this
.
item
,
this
.
selected
:
false
})
:
super
(
key:
key
)
{
assert
(
animation
!=
null
);
assert
(
item
!=
null
&&
item
>=
0
);
assert
(
selected
!=
null
);
}
final
Animation
<
double
>
animation
;
final
VoidCallback
onTap
;
final
int
item
;
final
bool
selected
;
@override
Widget
build
(
BuildContext
context
)
{
TextStyle
textStyle
=
Theme
.
of
(
context
).
textTheme
.
display1
;
if
(
selected
)
textStyle
=
textStyle
.
copyWith
(
color:
Colors
.
lightGreenAccent
[
400
]);
return
new
Padding
(
padding:
const
EdgeInsets
.
all
(
2.0
),
child:
new
SizeTransition
(
axis:
Axis
.
vertical
,
sizeFactor:
animation
,
child:
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
new
SizedBox
(
height:
128.0
,
child:
new
Card
(
color:
Colors
.
primaries
[
item
%
Colors
.
primaries
.
length
],
child:
new
Center
(
child:
new
Text
(
'Item
$item
'
,
style:
textStyle
),
),
),
),
),
),
);
}
}
/// Keeps a Dart List in sync with an AnimatedList.
///
/// The [insert] and [removeAt] methods apply to both the internal list and the
/// animated list that belongs to [listKey].
///
/// This class only exposes as much of the Dart List API as is needed by the
/// sample app. More list methods are easily added, however methods that mutate the
/// list must make the same changes to the animated list in terms of
/// [AnimatedListState.insertItem] and [AnimatedList.removeItem].
class
ListModel
<
E
>
{
ListModel
({
@required
this
.
listKey
,
@required
this
.
removedItemBuilder
,
Iterable
<
E
>
initialItems
,
})
:
_items
=
new
List
<
E
>.
from
(
initialItems
??
<
E
>[])
{
assert
(
listKey
!=
null
);
assert
(
removedItemBuilder
!=
null
);
}
final
GlobalKey
<
AnimatedListState
>
listKey
;
final
dynamic
removedItemBuilder
;
final
List
<
E
>
_items
;
AnimatedListState
get
_animatedList
=>
listKey
.
currentState
;
void
insert
(
int
index
,
E
item
)
{
_items
.
insert
(
index
,
item
);
_animatedList
.
insertItem
(
index
);
}
E
removeAt
(
int
index
)
{
final
E
removedItem
=
_items
.
removeAt
(
index
);
if
(
removedItem
!=
null
)
{
_animatedList
.
removeItem
(
index
,
(
BuildContext
context
,
Animation
<
double
>
animation
)
{
return
removedItemBuilder
(
removedItem
,
context
,
animation
);
});
}
return
removedItem
;
}
int
get
length
=>
_items
.
length
;
E
operator
[](
int
index
)
=>
_items
[
index
];
int
indexOf
(
E
item
)
=>
_items
.
indexOf
(
item
);
}
class
AnimatedListSample
extends
StatefulWidget
{
@override
_AnimatedListSampleState
createState
()
=>
new
_AnimatedListSampleState
();
...
...
@@ -177,28 +73,125 @@ class _AnimatedListSampleState extends State<AnimatedListSample> {
@override
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'AnimatedList'
),
actions:
<
Widget
>[
new
IconButton
(
icon:
const
Icon
(
Icons
.
add_circle
),
onPressed:
_insert
,
tooltip:
'insert a new item'
,
),
new
IconButton
(
icon:
const
Icon
(
Icons
.
remove_circle
),
onPressed:
_remove
,
tooltip:
'remove the selected item'
,
return
new
MaterialApp
(
home:
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'AnimatedList'
),
actions:
<
Widget
>[
new
IconButton
(
icon:
const
Icon
(
Icons
.
add_circle
),
onPressed:
_insert
,
tooltip:
'insert a new item'
,
),
new
IconButton
(
icon:
const
Icon
(
Icons
.
remove_circle
),
onPressed:
_remove
,
tooltip:
'remove the selected item'
,
),
],
),
body:
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
AnimatedList
(
key:
_listKey
,
initialItemCount:
_list
.
length
,
itemBuilder:
_buildItem
,
),
]
,
)
,
),
body:
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
AnimatedList
(
key:
_listKey
,
initialItemCount:
_list
.
length
,
itemBuilder:
_buildItem
,
);
}
}
/// Keeps a Dart List in sync with an AnimatedList.
///
/// The [insert] and [removeAt] methods apply to both the internal list and the
/// animated list that belongs to [listKey].
///
/// This class only exposes as much of the Dart List API as is needed by the
/// sample app. More list methods are easily added, however methods that mutate the
/// list must make the same changes to the animated list in terms of
/// [AnimatedListState.insertItem] and [AnimatedList.removeItem].
class
ListModel
<
E
>
{
ListModel
({
@required
this
.
listKey
,
@required
this
.
removedItemBuilder
,
Iterable
<
E
>
initialItems
,
})
:
_items
=
new
List
<
E
>.
from
(
initialItems
??
<
E
>[])
{
assert
(
listKey
!=
null
);
assert
(
removedItemBuilder
!=
null
);
}
final
GlobalKey
<
AnimatedListState
>
listKey
;
final
dynamic
removedItemBuilder
;
final
List
<
E
>
_items
;
AnimatedListState
get
_animatedList
=>
listKey
.
currentState
;
void
insert
(
int
index
,
E
item
)
{
_items
.
insert
(
index
,
item
);
_animatedList
.
insertItem
(
index
);
}
E
removeAt
(
int
index
)
{
final
E
removedItem
=
_items
.
removeAt
(
index
);
if
(
removedItem
!=
null
)
{
_animatedList
.
removeItem
(
index
,
(
BuildContext
context
,
Animation
<
double
>
animation
)
{
return
removedItemBuilder
(
removedItem
,
context
,
animation
);
});
}
return
removedItem
;
}
int
get
length
=>
_items
.
length
;
E
operator
[](
int
index
)
=>
_items
[
index
];
int
indexOf
(
E
item
)
=>
_items
.
indexOf
(
item
);
}
/// Displays its integer item as 'item N' on a Card whose color is based on
/// the item's value. The text is displayed in bright green if selected is true.
/// This widget's height is based on the animation parameter, it varies
/// from 0 to 128 as the animation varies from 0.0 to 1.0.
class
CardItem
extends
StatelessWidget
{
CardItem
({
Key
key
,
@required
this
.
animation
,
this
.
onTap
,
@required
this
.
item
,
this
.
selected
:
false
})
:
super
(
key:
key
)
{
assert
(
animation
!=
null
);
assert
(
item
!=
null
&&
item
>=
0
);
assert
(
selected
!=
null
);
}
final
Animation
<
double
>
animation
;
final
VoidCallback
onTap
;
final
int
item
;
final
bool
selected
;
@override
Widget
build
(
BuildContext
context
)
{
TextStyle
textStyle
=
Theme
.
of
(
context
).
textTheme
.
display1
;
if
(
selected
)
textStyle
=
textStyle
.
copyWith
(
color:
Colors
.
lightGreenAccent
[
400
]);
return
new
Padding
(
padding:
const
EdgeInsets
.
all
(
2.0
),
child:
new
SizeTransition
(
axis:
Axis
.
vertical
,
sizeFactor:
animation
,
child:
new
GestureDetector
(
behavior:
HitTestBehavior
.
opaque
,
onTap:
onTap
,
child:
new
SizedBox
(
height:
128.0
,
child:
new
Card
(
color:
Colors
.
primaries
[
item
%
Colors
.
primaries
.
length
],
child:
new
Center
(
child:
new
Text
(
'Item
$item
'
,
style:
textStyle
),
),
),
),
),
),
);
...
...
@@ -206,5 +199,32 @@ class _AnimatedListSampleState extends State<AnimatedListSample> {
}
void
main
(
)
{
runApp
(
new
MaterialApp
(
home:
new
AnimatedListSample
()
));
runApp
(
new
AnimatedListSample
(
));
}
/*
Sample Catalog
Title: AnimatedList
Summary: In this app an AnimatedList displays a list of cards which stays
in sync with an app-specific ListModel. When an item is added to or removed
from the model, a corresponding card items animate in or out of view
in the animated list.
Description:
Tap an item to select it, tap it again to unselect. Tap '+' to insert at the
selected item, '-' to remove the selected item. The tap handlers add or
remove items from a `ListModel<E>`, a simple encapsulation of `List<E>`
that keeps the AnimatedList in sync. The list model has a GlobalKey for
its animated list. It uses the key to call the insertItem and removeItem
methods defined by AnimatedListState.
Classes: AnimatedList, AnimatedListState
Sample: AnimatedListSample
See also:
- The "Components-Lists: Controls" section of the material design specification:
<https://material.io/guidelines/components/lists-controls.html#>
*/
examples/catalog/lib/app_bar_bottom.dart
View file @
2a140a77
...
...
@@ -4,17 +4,76 @@
import
'package:flutter/material.dart'
;
/// Sample Catalog: AppBar with a custom bottom widget.
///
/// The bottom widget's TabPageSelector displays the relative position of the
/// selected choice. The arrow buttons in the toolbar part of the app bar
/// select the previous or the next choice.
///
/// Sample classes: [AppBar], [TabController], [TabPageSelector], [Scaffold], [TabBarView].
///
/// See also:
/// * The "Components-Tabs" section of the material design specification:
/// <https://material.io/guidelines/components/tabs.html>
class
AppBarBottomSample
extends
StatefulWidget
{
@override
_AppBarBottomSampleState
createState
()
=>
new
_AppBarBottomSampleState
();
}
class
_AppBarBottomSampleState
extends
State
<
AppBarBottomSample
>
with
SingleTickerProviderStateMixin
{
TabController
_tabController
;
@override
void
initState
()
{
super
.
initState
();
_tabController
=
new
TabController
(
vsync:
this
,
length:
choices
.
length
);
}
@override
void
dispose
()
{
_tabController
.
dispose
();
super
.
dispose
();
}
void
_nextPage
(
int
delta
)
{
final
int
newIndex
=
_tabController
.
index
+
delta
;
if
(
newIndex
<
0
||
newIndex
>=
_tabController
.
length
)
return
;
_tabController
.
animateTo
(
newIndex
);
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
MaterialApp
(
home:
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'AppBar Bottom Widget'
),
leading:
new
IconButton
(
tooltip:
'Previous choice'
,
icon:
const
Icon
(
Icons
.
arrow_back
),
onPressed:
()
{
_nextPage
(-
1
);
},
),
actions:
<
Widget
>[
new
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_forward
),
tooltip:
'Next choice'
,
onPressed:
()
{
_nextPage
(
1
);
},
),
],
bottom:
new
PreferredSize
(
preferredSize:
const
Size
.
fromHeight
(
48.0
),
child:
new
Theme
(
data:
Theme
.
of
(
context
).
copyWith
(
accentColor:
Colors
.
white
),
child:
new
Container
(
height:
48.0
,
alignment:
FractionalOffset
.
center
,
child:
new
TabPageSelector
(
controller:
_tabController
),
),
),
),
),
body:
new
TabBarView
(
controller:
_tabController
,
children:
choices
.
map
((
Choice
choice
)
{
return
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
ChoiceCard
(
choice:
choice
),
);
}).
toList
(),
),
),
);
}
}
class
Choice
{
const
Choice
({
this
.
title
,
this
.
icon
});
...
...
@@ -55,75 +114,29 @@ class ChoiceCard extends StatelessWidget {
}
}
class
AppBarBottomSample
extends
StatefulWidget
{
@override
_AppBarBottomSampleState
createState
()
=>
new
_AppBarBottomSampleState
();
void
main
(
)
{
runApp
(
new
AppBarBottomSample
());
}
class
_AppBarBottomSampleState
extends
State
<
AppBarBottomSample
>
with
SingleTickerProviderStateMixin
{
TabController
_tabController
;
/*
Sample Catalog
@override
void
initState
()
{
super
.
initState
();
_tabController
=
new
TabController
(
vsync:
this
,
length:
choices
.
length
);
}
Title: AppBar with a custom bottom widget.
@override
void
dispose
()
{
_tabController
.
dispose
();
super
.
dispose
();
}
Summary: The AppBar's bottom widget is often a TabBar however any widget with a
PreferredSize can be used.
void
_nextPage
(
int
delta
)
{
final
int
newIndex
=
_tabController
.
index
+
delta
;
if
(
newIndex
<
0
||
newIndex
>=
_tabController
.
length
)
return
;
_tabController
.
animateTo
(
newIndex
);
}
Description:
In this app, the app bar's bottom widget is a TabPageSelector
that displays the relative position of the selected page in the app's
TabBarView. The arrow buttons in the toolbar part of the app bar select
the previous or the next choice.
@override
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'AppBar Bottom Widget'
),
leading:
new
IconButton
(
tooltip:
'Previous choice'
,
icon:
const
Icon
(
Icons
.
arrow_back
),
onPressed:
()
{
_nextPage
(-
1
);
},
),
actions:
<
Widget
>[
new
IconButton
(
icon:
const
Icon
(
Icons
.
arrow_forward
),
tooltip:
'Next choice'
,
onPressed:
()
{
_nextPage
(
1
);
},
),
],
bottom:
new
PreferredSize
(
preferredSize:
const
Size
.
fromHeight
(
48.0
),
child:
new
Theme
(
data:
Theme
.
of
(
context
).
copyWith
(
accentColor:
Colors
.
white
),
child:
new
Container
(
height:
48.0
,
alignment:
FractionalOffset
.
center
,
child:
new
TabPageSelector
(
controller:
_tabController
),
),
),
),
),
body:
new
TabBarView
(
controller:
_tabController
,
children:
choices
.
map
((
Choice
choice
)
{
return
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
ChoiceCard
(
choice:
choice
),
);
}).
toList
(),
),
);
}
}
Classes: AppBar, PreferredSize, TabBarView, TabController
void
main
(
)
{
runApp
(
new
MaterialApp
(
home:
new
AppBarBottomSample
()));
}
Sample: AppBarBottomSample
See also:
- The "Components-Tabs" section of the material design specification:
<https://material.io/guidelines/components/tabs.html>
*/
examples/catalog/lib/basic_app_bar.dart
View file @
2a140a77
...
...
@@ -4,17 +4,57 @@
import
'package:flutter/material.dart'
;
/// Sample Catalog: Basic AppBar
///
/// An AppBar with a title, actions, and an overflow menu. One of the app's
/// choices can be selected action buttons or the menu.
///
/// Sample classes: [AppBar], [IconButton], [PopupMenuButton], [Scaffold].
///
/// See also:
///
/// * The "Layout-Structure" section of the material design specification:
/// <https://material.io/guidelines/layout/structure.html#structure-app-bar>
// This app is a stateful, it tracks the user's current choice.
class
BasicAppBarSample
extends
StatefulWidget
{
@override
_BasicAppBarSampleState
createState
()
=>
new
_BasicAppBarSampleState
();
}
class
_BasicAppBarSampleState
extends
State
<
BasicAppBarSample
>
{
Choice
_selectedChoice
=
choices
[
0
];
// The app's "state".
void
_select
(
Choice
choice
)
{
setState
(()
{
// Causes the app to rebuild with the new _selectedChoice.
_selectedChoice
=
choice
;
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
MaterialApp
(
home:
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'Basic AppBar'
),
actions:
<
Widget
>[
new
IconButton
(
// action button
icon:
new
Icon
(
choices
[
0
].
icon
),
onPressed:
()
{
_select
(
choices
[
0
]);
},
),
new
IconButton
(
// action button
icon:
new
Icon
(
choices
[
1
].
icon
),
onPressed:
()
{
_select
(
choices
[
1
]);
},
),
new
PopupMenuButton
<
Choice
>(
// overflow menu
onSelected:
_select
,
itemBuilder:
(
BuildContext
context
)
{
return
choices
.
skip
(
2
).
map
((
Choice
choice
)
{
return
new
PopupMenuItem
<
Choice
>(
value:
choice
,
child:
new
Text
(
choice
.
title
),
);
}).
toList
();
},
),
],
),
body:
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
ChoiceCard
(
choice:
_selectedChoice
),
),
),
);
}
}
class
Choice
{
const
Choice
({
this
.
title
,
this
.
icon
});
...
...
@@ -55,55 +95,28 @@ class ChoiceCard extends StatelessWidget {
}
}
class
BasicAppBarSample
extends
StatefulWidget
{
@override
_BasicAppBarSampleState
createState
()
=>
new
_BasicAppBarSampleState
();
void
main
(
)
{
runApp
(
new
BasicAppBarSample
());
}
class
_BasicAppBarSampleState
extends
State
<
BasicAppBarSample
>
{
Choice
_selectedChoice
=
choices
[
0
];
/*
Sample Catalog
void
_select
(
Choice
choice
)
{
setState
(()
{
_selectedChoice
=
choice
;
});
}
Title: AppBar Basics
@override
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'Basic AppBar'
),
actions:
<
Widget
>[
new
IconButton
(
icon:
new
Icon
(
choices
[
0
].
icon
),
onPressed:
()
{
_select
(
choices
[
0
]);
},
),
new
IconButton
(
icon:
new
Icon
(
choices
[
1
].
icon
),
onPressed:
()
{
_select
(
choices
[
1
]);
},
),
new
PopupMenuButton
<
Choice
>(
onSelected:
_select
,
itemBuilder:
(
BuildContext
context
)
{
return
choices
.
skip
(
2
).
map
((
Choice
choice
)
{
return
new
PopupMenuItem
<
Choice
>(
value:
choice
,
child:
new
Text
(
choice
.
title
),
);
}).
toList
();
},
),
],
),
body:
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
ChoiceCard
(
choice:
_selectedChoice
),
),
);
}
}
Summary: An AppBar with a title, actions, and an overflow dropdown menu.
One of the app's choices can be selected with an action button or the menu.
void
main
(
)
{
runApp
(
new
MaterialApp
(
home:
new
BasicAppBarSample
()));
}
Description:
An app that displays one of a half dozen choices with an icon and a title.
The two most common choices are available as action buttons and the remaining
choices are included in the overflow dropdow menu.
Classes: AppBar, IconButton, PopupMenuButton, Scaffold
Sample: BasicAppBarSample
See also:
- The "Layout-Structure" section of the material design specification:
<https://material.io/guidelines/layout/structure.html#structure-app-bar>
*/
examples/catalog/lib/expansion_tile_sample.dart
View file @
2a140a77
...
...
@@ -4,12 +4,31 @@
import
'package:flutter/material.dart'
;
class
ExpansionTileSample
extends
StatelessWidget
{
@override
Widget
build
(
BuildContext
context
)
{
return
new
MaterialApp
(
home:
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'ExpansionTile'
),
),
body:
new
ListView
.
builder
(
itemBuilder:
(
BuildContext
context
,
int
index
)
=>
new
EntryItem
(
data
[
index
]),
itemCount:
data
.
length
,
),
),
);
}
}
// One entry in the multilevel list displayed by this app.
class
Entry
{
Entry
(
this
.
title
,
[
this
.
children
=
const
<
Entry
>[]]);
final
String
title
;
final
List
<
Entry
>
children
;
}
// The entire multilevel list displayed by this app.
final
List
<
Entry
>
data
=
<
Entry
>[
new
Entry
(
'Chapter A'
,
<
Entry
>[
...
...
@@ -46,6 +65,8 @@ final List<Entry> data = <Entry>[
),
];
// Displays one Entry. If the entry has children then it's displayed
// with an ExpansionTile.
class
EntryItem
extends
StatelessWidget
{
EntryItem
(
this
.
entry
);
...
...
@@ -67,21 +88,31 @@ class EntryItem extends StatelessWidget {
}
}
class
ExpansionTileSample
extends
StatelessWidget
{
@override
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'ExpansionTile'
),
),
body:
new
ListView
.
builder
(
itemBuilder:
(
BuildContext
context
,
int
index
)
=>
new
EntryItem
(
data
[
index
]),
itemCount:
data
.
length
,
),
);
}
}
void
main
(
)
{
runApp
(
new
MaterialApp
(
home:
new
ExpansionTileSample
()
));
runApp
(
new
ExpansionTileSample
(
));
}
/*
Sample Catalog
Title: ExpansionTile
Summary: ExpansionTiles can used to produce two-level or multi-level lists.
When displayed within a scrollable that creates its list items lazily,
like a scrollable list created with `ListView.builder()`, they can be quite
efficient, particularly for material design "expand/collapse" lists.
Description:
This app displays hierarchical data with ExpansionTiles. Tapping a tile
expands or collapses the view of its children. When a tile is collapsed
its children are disposed so that the widget footprint of the list only
reflects what's visible.
Classes: ExpansionTile, ListView
Sample: ExpansionTileSample
See also:
- The "expand/collapse" part of the material design specification:
<https://material.io/guidelines/components/lists-controls.html#lists-controls-types-of-list-controls>
*/
examples/catalog/lib/tabbed_app_bar.dart
View file @
2a140a77
...
...
@@ -4,16 +4,38 @@
import
'package:flutter/material.dart'
;
/// Sample Catalog: Tabbed AppBar
///
/// A basic app bar with a tab bar at the bottom. One of the app's choices can be
/// selected by tapping the tabs or by swiping the tab bar view.
///
/// Sample classes: [AppBar], [DefaultTabController], [TabBar], [Scaffold], [TabBarView].
///
/// See also:
/// * The "Components-Tabs" section of the material design specification:
/// <https://material.io/guidelines/components/tabs.html>
class
TabbedAppBarSample
extends
StatelessWidget
{
@override
Widget
build
(
BuildContext
context
)
{
return
new
MaterialApp
(
home:
new
DefaultTabController
(
length:
choices
.
length
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'Tabbed AppBar'
),
bottom:
new
TabBar
(
isScrollable:
true
,
tabs:
choices
.
map
((
Choice
choice
)
{
return
new
Tab
(
text:
choice
.
title
,
icon:
new
Icon
(
choice
.
icon
),
);
}).
toList
(),
),
),
body:
new
TabBarView
(
children:
choices
.
map
((
Choice
choice
)
{
return
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
ChoiceCard
(
choice:
choice
),
);
}).
toList
(),
),
),
),
);
}
}
class
Choice
{
const
Choice
({
this
.
title
,
this
.
icon
});
...
...
@@ -54,37 +76,27 @@ class ChoiceCard extends StatelessWidget {
}
}
class
TabbedAppBarSample
extends
StatelessWidget
{
@override
Widget
build
(
BuildContext
context
)
{
return
new
DefaultTabController
(
length:
choices
.
length
,
child:
new
Scaffold
(
appBar:
new
AppBar
(
title:
const
Text
(
'Tabbed AppBar'
),
bottom:
new
TabBar
(
isScrollable:
true
,
tabs:
choices
.
map
((
Choice
choice
)
{
return
new
Tab
(
text:
choice
.
title
,
icon:
new
Icon
(
choice
.
icon
),
);
}).
toList
(),
),
),
body:
new
TabBarView
(
children:
choices
.
map
((
Choice
choice
)
{
return
new
Padding
(
padding:
const
EdgeInsets
.
all
(
16.0
),
child:
new
ChoiceCard
(
choice:
choice
),
);
}).
toList
(),
),
),
);
}
}
void
main
(
)
{
runApp
(
new
MaterialApp
(
home:
new
TabbedAppBarSample
()
));
runApp
(
new
TabbedAppBarSample
(
));
}
/*
Sample Catalog
Title: Tabbed AppBar
Summary: An AppBar can include a TabBar as its bottom widget.
Description:
A TabBar can be used to navigate among the pages displayed in a TabBarView.
Although a TabBar is an ordinary widget that can appear, it's most often
included in the application's AppBar.
Classes: AppBar, DefaultTabController, TabBar, Scaffold, TabBarView
Sample: TabbedAppBarSample
See also:
- The "Components-Tabs" section of the material design specification:
<https://material.io/guidelines/components/tabs.html>
*/
examples/catalog/test/
smoke
_test.dart
→
examples/catalog/test/
animated_list
_test.dart
View file @
2a140a77
File moved
examples/catalog/test_driver/README.md
0 → 100644
View file @
2a140a77
The screenshot_test.dart file was generated by ../bin/sample_page.dart. It should not be checked in.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment