// 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. 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( 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: 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)), const Text( 'Buy this cool color', style: TextStyle( color: Color(0xFF8E8E93), 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: const Color(0xFFD9D9D9), ), ], ); } } 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: const TextStyle( color: Color(0xFF8E8E93), 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>[ 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: const BoxDecoration( color: Color(0xFFE5E5E5), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: const <Widget>[ Text( 'SUPPORT TICKET', style: TextStyle( color: Color(0xFF646464), letterSpacing: -0.9, fontSize: 14.0, fontWeight: FontWeight.w500, ), ), Text( 'Show More', style: TextStyle( color: Color(0xFF646464), letterSpacing: -0.6, fontSize: 12.0, fontWeight: FontWeight.w500, ), ), ], ), ), ), Container( decoration: const BoxDecoration( color: Color(0xFFF3F3F3), ), 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) { return Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(18.0)), color: color == Tab2ConversationBubbleColor.blue ? CupertinoColors.activeBlue : CupertinoColors.lightBackgroundGray, ), 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: color == Tab2ConversationBubbleColor.blue ? CupertinoColors.white : CupertinoColors.black, 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, 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, ), child: DecoratedBox( decoration: BoxDecoration( color: CupertinoTheme.of(context).brightness == Brightness.light ? CupertinoColors.extraLightBackgroundGray : CupertinoColors.darkBackgroundGray, ), 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); }, ), ], ), ), ); } }