flutter/examples/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
Ian Hickson 449f4a6673
License update (#45373)
* Update project.pbxproj files to say Flutter rather than Chromium

Also, the templates now have an empty organization so that we don't cause people to give their apps a Flutter copyright.

* Update the copyright notice checker to require a standard notice on all files

* Update copyrights on Dart files. (This was a mechanical commit.)

* Fix weird license headers on Dart files that deviate from our conventions; relicense Shrine.

Some were already marked "The Flutter Authors", not clear why. Their
dates have been normalized. Some were missing the blank line after the
license. Some were randomly different in trivial ways for no apparent
reason (e.g. missing the trailing period).

* Clean up the copyrights in non-Dart files. (Manual edits.)

Also, make sure templates don't have copyrights.

* Fix some more ORGANIZATIONNAMEs
2019-11-27 15:04:02 -08:00

813 lines
26 KiB
Dart

// Copyright 2014 The Flutter 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/cupertino.dart';
import 'package:flutter/material.dart';
import '../../gallery/demo.dart';
const String _kGalleryAssetsPackage = 'flutter_gallery_assets';
const List<Color> coolColors = <Color>[
Color.fromARGB(255, 255, 59, 48),
Color.fromARGB(255, 255, 149, 0),
Color.fromARGB(255, 255, 204, 0),
Color.fromARGB(255, 76, 217, 100),
Color.fromARGB(255, 90, 200, 250),
Color.fromARGB(255, 0, 122, 255),
Color.fromARGB(255, 88, 86, 214),
Color.fromARGB(255, 255, 45, 85),
];
const List<String> coolColorNames = <String>[
'Sarcoline', 'Coquelicot', 'Smaragdine', 'Mikado', 'Glaucous', 'Wenge',
'Fulvous', 'Xanadu', 'Falu', 'Eburnean', 'Amaranth', 'Australien',
'Banan', 'Falu', 'Gingerline', 'Incarnadine', 'Labrador', 'Nattier',
'Pervenche', 'Sinoper', 'Verditer', 'Watchet', 'Zaffre',
];
const int _kChildCount = 50;
class CupertinoNavigationDemo extends StatelessWidget {
CupertinoNavigationDemo()
: colorItems = List<Color>.generate(_kChildCount, (int index) {
return coolColors[math.Random().nextInt(coolColors.length)];
}) ,
colorNameItems = List<String>.generate(_kChildCount, (int index) {
return coolColorNames[math.Random().nextInt(coolColorNames.length)];
});
static const String routeName = '/cupertino/navigation';
final List<Color> colorItems;
final List<String> colorNameItems;
@override
Widget build(BuildContext context) {
return WillPopScope(
// Prevent swipe popping of this page. Use explicit exit buttons only.
onWillPop: () => Future<bool>.value(true),
child: DefaultTextStyle(
style: CupertinoTheme.of(context).textTheme.textStyle,
child: CupertinoTabScaffold(
tabBar: CupertinoTabBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.home),
title: Text('Home'),
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.conversation_bubble),
title: Text('Support'),
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.profile_circled),
title: Text('Profile'),
),
],
),
tabBuilder: (BuildContext context, int index) {
assert(index >= 0 && index <= 2);
switch (index) {
case 0:
return CupertinoTabView(
builder: (BuildContext context) {
return CupertinoDemoTab1(
colorItems: colorItems,
colorNameItems: colorNameItems,
);
},
defaultTitle: 'Colors',
);
break;
case 1:
return CupertinoTabView(
builder: (BuildContext context) => CupertinoDemoTab2(),
defaultTitle: 'Support Chat',
);
break;
case 2:
return CupertinoTabView(
builder: (BuildContext context) => CupertinoDemoTab3(),
defaultTitle: 'Account',
);
break;
}
return null;
},
),
),
);
}
}
class ExitButton extends StatelessWidget {
const ExitButton();
@override
Widget build(BuildContext context) {
return CupertinoButton(
padding: EdgeInsets.zero,
child: const Tooltip(
message: 'Back',
child: Text('Exit'),
excludeFromSemantics: true,
),
onPressed: () {
// The demo is on the root navigator.
Navigator.of(context, rootNavigator: true).pop();
},
);
}
}
final Widget trailingButtons = Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CupertinoDemoDocumentationButton(CupertinoNavigationDemo.routeName),
const Padding(padding: EdgeInsets.only(left: 8.0)),
const ExitButton(),
],
);
class CupertinoDemoTab1 extends StatelessWidget {
const CupertinoDemoTab1({this.colorItems, this.colorNameItems});
final List<Color> colorItems;
final List<String> colorNameItems;
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
backgroundColor: CupertinoColors.systemGroupedBackground,
child: CustomScrollView(
semanticChildCount: _kChildCount,
slivers: <Widget>[
CupertinoSliverNavigationBar(
trailing: trailingButtons,
),
SliverPadding(
// Top media padding consumed by CupertinoSliverNavigationBar.
// Left/Right media padding consumed by Tab1RowItem.
padding: MediaQuery.of(context).removePadding(
removeTop: true,
removeLeft: true,
removeRight: true,
).padding,
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Tab1RowItem(
index: index,
lastItem: index == _kChildCount - 1,
color: colorItems[index],
colorName: colorNameItems[index],
);
},
childCount: _kChildCount,
),
),
),
],
),
);
}
}
class Tab1RowItem extends StatelessWidget {
const Tab1RowItem({this.index, this.lastItem, this.color, this.colorName});
final int index;
final bool lastItem;
final Color color;
final String colorName;
@override
Widget build(BuildContext context) {
final Widget row = GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
Navigator.of(context).push(CupertinoPageRoute<void>(
title: colorName,
builder: (BuildContext context) => Tab1ItemPage(
color: color,
colorName: colorName,
index: index,
),
));
},
child: Container(
color: CupertinoDynamicColor.resolve(CupertinoColors.systemBackground, context),
child: SafeArea(
top: false,
bottom: false,
child: Padding(
padding: const EdgeInsets.only(left: 16.0, top: 8.0, bottom: 8.0, right: 8.0),
child: Row(
children: <Widget>[
Container(
height: 60.0,
width: 60.0,
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(8.0),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(colorName),
const Padding(padding: EdgeInsets.only(top: 8.0)),
Text(
'Buy this cool color',
style: TextStyle(
color: CupertinoDynamicColor.resolve(CupertinoColors.secondaryLabel, context),
fontSize: 13.0,
fontWeight: FontWeight.w300,
),
),
],
),
),
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.plus_circled,
semanticLabel: 'Add',
),
onPressed: () { },
),
CupertinoButton(
padding: EdgeInsets.zero,
child: const Icon(CupertinoIcons.share,
semanticLabel: 'Share',
),
onPressed: () { },
),
],
),
),
),
),
);
if (lastItem) {
return row;
}
return Column(
children: <Widget>[
row,
Container(
height: 1.0,
color: CupertinoDynamicColor.resolve(CupertinoColors.separator, context),
),
],
);
}
}
class Tab1ItemPage extends StatefulWidget {
const Tab1ItemPage({this.color, this.colorName, this.index});
final Color color;
final String colorName;
final int index;
@override
State<StatefulWidget> createState() => Tab1ItemPageState();
}
class Tab1ItemPageState extends State<Tab1ItemPage> {
@override
void initState() {
super.initState();
relatedColors = List<Color>.generate(10, (int index) {
final math.Random random = math.Random();
return Color.fromARGB(
255,
(widget.color.red + random.nextInt(100) - 50).clamp(0, 255),
(widget.color.green + random.nextInt(100) - 50).clamp(0, 255),
(widget.color.blue + random.nextInt(100) - 50).clamp(0, 255),
);
});
}
List<Color> relatedColors;
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
trailing: ExitButton(),
),
child: SafeArea(
top: false,
bottom: false,
child: ListView(
children: <Widget>[
const Padding(padding: EdgeInsets.only(top: 16.0)),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
height: 128.0,
width: 128.0,
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(24.0),
),
),
const Padding(padding: EdgeInsets.only(left: 18.0)),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
widget.colorName,
style: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
),
const Padding(padding: EdgeInsets.only(top: 6.0)),
Text(
'Item number ${widget.index}',
style: TextStyle(
color: CupertinoDynamicColor.resolve(CupertinoColors.secondaryLabel, context),
fontSize: 16.0,
fontWeight: FontWeight.w100,
),
),
const Padding(padding: EdgeInsets.only(top: 20.0)),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CupertinoButton.filled(
minSize: 30.0,
padding: const EdgeInsets.symmetric(horizontal: 24.0),
borderRadius: BorderRadius.circular(32.0),
child: const Text(
'GET',
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.28,
),
),
onPressed: () { },
),
CupertinoButton.filled(
minSize: 30.0,
padding: EdgeInsets.zero,
borderRadius: BorderRadius.circular(32.0),
child: const Icon(CupertinoIcons.ellipsis),
onPressed: () { },
),
],
),
],
),
),
],
),
),
const Padding(
padding: EdgeInsets.only(left: 16.0, top: 28.0, bottom: 8.0),
child: Text(
'USERS ALSO LIKED',
style: TextStyle(
color: Color(0xFF646464),
letterSpacing: -0.60,
fontSize: 15.0,
fontWeight: FontWeight.w500,
),
),
),
SizedBox(
height: 200.0,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemExtent: 160.0,
itemBuilder: (BuildContext context, int index) {
return Padding(
padding: const EdgeInsets.only(left: 16.0),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
color: relatedColors[index],
),
child: Center(
child: CupertinoButton(
child: const Icon(
CupertinoIcons.plus_circled,
color: CupertinoColors.white,
size: 36.0,
),
onPressed: () { },
),
),
),
);
},
),
),
],
),
),
);
}
}
class CupertinoDemoTab2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
trailing: trailingButtons,
),
child: CupertinoScrollbar(
child: ListView(
children: <Widget>[
CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: Tab2Header(),
),
...buildTab2Conversation(),
],
),
),
);
}
}
class Tab2Header extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: SafeArea(
top: false,
bottom: false,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16.0)),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
decoration: BoxDecoration(
color: CupertinoDynamicColor.resolve(CupertinoColors.systemFill, context),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'SUPPORT TICKET',
style: TextStyle(
color: CupertinoDynamicColor.resolve(CupertinoColors.secondaryLabel, context),
letterSpacing: -0.9,
fontSize: 14.0,
fontWeight: FontWeight.w500,
),
),
Text(
'Show More',
style: TextStyle(
color: CupertinoDynamicColor.resolve(CupertinoColors.secondaryLabel, context),
letterSpacing: -0.6,
fontSize: 12.0,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
Container(
decoration: BoxDecoration(
color: CupertinoDynamicColor.resolve(CupertinoColors.quaternarySystemFill, context),
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
const Text(
'Product or product packaging damaged during transit',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w700,
letterSpacing: -0.46,
),
),
const Padding(padding: EdgeInsets.only(top: 16.0)),
const Text(
'REVIEWERS',
style: TextStyle(
color: Color(0xFF646464),
fontSize: 12.0,
letterSpacing: -0.6,
fontWeight: FontWeight.w500,
),
),
const Padding(padding: EdgeInsets.only(top: 8.0)),
Row(
children: <Widget>[
Container(
width: 44.0,
height: 44.0,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
'people/square/trevor.png',
package: _kGalleryAssetsPackage,
),
),
shape: BoxShape.circle,
),
),
const Padding(padding: EdgeInsets.only(left: 8.0)),
Container(
width: 44.0,
height: 44.0,
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(
'people/square/sandra.png',
package: _kGalleryAssetsPackage,
),
),
shape: BoxShape.circle,
),
),
const Padding(padding: EdgeInsets.only(left: 2.0)),
const Icon(
CupertinoIcons.check_mark_circled,
color: Color(0xFF646464),
size: 20.0,
),
],
),
],
),
),
),
],
),
),
),
);
}
}
enum Tab2ConversationBubbleColor {
blue,
gray,
}
class Tab2ConversationBubble extends StatelessWidget {
const Tab2ConversationBubble({this.text, this.color});
final String text;
final Tab2ConversationBubbleColor color;
@override
Widget build(BuildContext context) {
Color backgroundColor;
Color foregroundColor;
switch (color) {
case Tab2ConversationBubbleColor.gray:
backgroundColor = CupertinoDynamicColor.resolve(CupertinoColors.systemFill, context);
foregroundColor = CupertinoDynamicColor.resolve(CupertinoColors.label, context);
break;
case Tab2ConversationBubbleColor.blue:
backgroundColor = CupertinoTheme.of(context).primaryColor;
foregroundColor = CupertinoColors.white;
break;
}
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(18.0)),
color: backgroundColor,
),
margin: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 8.0),
padding: const EdgeInsets.symmetric(horizontal: 14.0, vertical: 10.0),
child: Text(
text,
style: TextStyle(
color: foregroundColor,
letterSpacing: -0.4,
fontSize: 15.0,
fontWeight: FontWeight.w400,
),
),
);
}
}
class Tab2ConversationAvatar extends StatelessWidget {
const Tab2ConversationAvatar({this.text, this.color});
final String text;
final Color color;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
colors: <Color>[
color,
Color.fromARGB(
color.alpha,
(color.red - 60).clamp(0, 255),
(color.green - 60).clamp(0, 255),
(color.blue - 60).clamp(0, 255),
),
],
),
),
margin: const EdgeInsets.only(left: 8.0, bottom: 8.0),
padding: const EdgeInsets.all(12.0),
child: Text(
text,
style: const TextStyle(
color: CupertinoColors.white,
fontSize: 13.0,
fontWeight: FontWeight.w500,
),
),
);
}
}
class Tab2ConversationRow extends StatelessWidget {
const Tab2ConversationRow({this.avatar, this.text});
final Tab2ConversationAvatar avatar;
final String text;
@override
Widget build(BuildContext context) {
final bool isSelf = avatar == null;
return SafeArea(
child: Row(
mainAxisAlignment: isSelf ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: isSelf ? CrossAxisAlignment.center : CrossAxisAlignment.end,
children: <Widget>[
if (avatar != null) avatar,
CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: Tab2ConversationBubble(
text: text,
color: isSelf
? Tab2ConversationBubbleColor.blue
: Tab2ConversationBubbleColor.gray,
),
),
],
),
);
}
}
List<Widget> buildTab2Conversation() {
return <Widget>[
const Tab2ConversationRow(
text: "My Xanadu doesn't look right",
),
const Tab2ConversationRow(
avatar: Tab2ConversationAvatar(
text: 'KL',
color: Color(0xFFFD5015),
),
text: "We'll rush you a new one.\nIt's gonna be incredible",
),
const Tab2ConversationRow(
text: 'Awesome thanks!',
),
const Tab2ConversationRow(
avatar: Tab2ConversationAvatar(
text: 'SJ',
color: Color(0xFF34CAD6),
),
text: "We'll send you our\nnewest Labrador too!",
),
const Tab2ConversationRow(
text: 'Yay',
),
const Tab2ConversationRow(
avatar: Tab2ConversationAvatar(
text: 'KL',
color: Color(0xFFFD5015),
),
text: "Actually there's one more thing...",
),
const Tab2ConversationRow(
text: "What's that?",
),
];
}
class CupertinoDemoTab3 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(trailing: trailingButtons),
backgroundColor: CupertinoColors.systemBackground,
child: ListView(
children: <Widget>[
const Padding(padding: EdgeInsets.only(top: 32.0)),
GestureDetector(
onTap: () {
Navigator.of(context, rootNavigator: true).push(
CupertinoPageRoute<bool>(
fullscreenDialog: true,
builder: (BuildContext context) => Tab3Dialog(),
),
);
},
child: Container(
decoration: BoxDecoration(
color: CupertinoTheme.of(context).scaffoldBackgroundColor,
border: const Border(
top: BorderSide(color: Color(0xFFBCBBC1), width: 0.0),
bottom: BorderSide(color: Color(0xFFBCBBC1), width: 0.0),
),
),
height: 44.0,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: SafeArea(
top: false,
bottom: false,
child: Row(
children: <Widget>[
Text(
'Sign in',
style: TextStyle(color: CupertinoTheme.of(context).primaryColor),
),
],
),
),
),
),
),
],
),
);
}
}
class Tab3Dialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
leading: CupertinoButton(
child: const Text('Cancel'),
padding: EdgeInsets.zero,
onPressed: () {
Navigator.of(context).pop(false);
},
),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Icon(
CupertinoIcons.profile_circled,
size: 160.0,
color: Color(0xFF646464),
),
const Padding(padding: EdgeInsets.only(top: 18.0)),
CupertinoButton.filled(
child: const Text('Sign in'),
onPressed: () {
Navigator.pop(context);
},
),
],
),
),
);
}
}