| // 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 'package:flutter/material.dart'; |
| import 'package:flutter/scheduler.dart' show timeDilation; |
| |
| void main() { |
| runApp( |
| const ComplexLayoutApp() |
| ); |
| } |
| |
| enum ScrollMode { complex, tile } |
| |
| class ComplexLayoutApp extends StatefulWidget { |
| const ComplexLayoutApp({Key key}) : super(key: key); |
| |
| @override |
| ComplexLayoutAppState createState() => ComplexLayoutAppState(); |
| |
| static ComplexLayoutAppState of(BuildContext context) => context.findAncestorStateOfType<ComplexLayoutAppState>(); |
| } |
| |
| class ComplexLayoutAppState extends State<ComplexLayoutApp> { |
| @override |
| Widget build(BuildContext context) { |
| return MaterialApp( |
| theme: lightTheme ? ThemeData.light() : ThemeData.dark(), |
| title: 'Advanced Layout', |
| home: scrollMode == ScrollMode.complex ? const ComplexLayout() : const TileScrollLayout()); |
| } |
| |
| bool _lightTheme = true; |
| bool get lightTheme => _lightTheme; |
| set lightTheme(bool value) { |
| setState(() { |
| _lightTheme = value; |
| }); |
| } |
| |
| ScrollMode _scrollMode = ScrollMode.complex; |
| ScrollMode get scrollMode => _scrollMode; |
| set scrollMode(ScrollMode mode) { |
| setState(() { |
| _scrollMode = mode; |
| }); |
| } |
| |
| void toggleAnimationSpeed() { |
| setState(() { |
| timeDilation = (timeDilation != 1.0) ? 1.0 : 5.0; |
| }); |
| } |
| } |
| |
| class TileScrollLayout extends StatelessWidget { |
| const TileScrollLayout({ Key key }) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Scaffold( |
| appBar: AppBar(title: const Text('Tile Scrolling Layout')), |
| body: ListView.builder( |
| key: const Key('tiles-scroll'), |
| itemCount: 200, |
| itemBuilder: (BuildContext context, int index) { |
| return Padding( |
| padding: const EdgeInsets.all(5.0), |
| child: Material( |
| elevation: (index % 5 + 1).toDouble(), |
| color: Colors.white, |
| child: const IconBar(), |
| ), |
| ); |
| }, |
| ), |
| drawer: const GalleryDrawer(), |
| ); |
| } |
| } |
| |
| class ComplexLayout extends StatefulWidget { |
| const ComplexLayout({ Key key }) : super(key: key); |
| |
| @override |
| ComplexLayoutState createState() => ComplexLayoutState(); |
| |
| static ComplexLayoutState of(BuildContext context) => context.findAncestorStateOfType<ComplexLayoutState>(); |
| } |
| |
| class ComplexLayoutState extends State<ComplexLayout> { |
| @override |
| Widget build(BuildContext context) { |
| return Scaffold( |
| appBar: AppBar( |
| title: const Text('Advanced Layout'), |
| actions: <Widget>[ |
| IconButton( |
| icon: const Icon(Icons.create), |
| tooltip: 'Search', |
| onPressed: () { |
| print('Pressed search'); |
| }, |
| ), |
| const TopBarMenu(), |
| ], |
| ), |
| body: Column( |
| children: <Widget>[ |
| Expanded( |
| child: ListView.builder( |
| key: const Key('complex-scroll'), // this key is used by the driver test |
| controller: ScrollController(), // So that the scroll offset can be tracked |
| itemBuilder: (BuildContext context, int index) { |
| if (index.isEven) |
| return FancyImageItem(index, key: PageStorageKey<int>(index)); |
| else |
| return FancyGalleryItem(index, key: PageStorageKey<int>(index)); |
| }, |
| ), |
| ), |
| const BottomBar(), |
| ], |
| ), |
| drawer: const GalleryDrawer(), |
| ); |
| } |
| } |
| |
| class TopBarMenu extends StatelessWidget { |
| const TopBarMenu({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return PopupMenuButton<String>( |
| onSelected: (String value) { print('Selected: $value'); }, |
| itemBuilder: (BuildContext context) => <PopupMenuItem<String>>[ |
| const PopupMenuItem<String>( |
| value: 'Friends', |
| child: MenuItemWithIcon(Icons.people, 'Friends', '5 new'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.event, 'Events', '12 upcoming'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.group, 'Groups', '14'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.image, 'Pictures', '12'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.near_me, 'Nearby', '33'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Friends', |
| child: MenuItemWithIcon(Icons.people, 'Friends', '5'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.event, 'Events', '12'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.group, 'Groups', '14'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.image, 'Pictures', '12'), |
| ), |
| const PopupMenuItem<String>( |
| value: 'Events', |
| child: MenuItemWithIcon(Icons.near_me, 'Nearby', '33'), |
| ), |
| ], |
| ); |
| } |
| } |
| |
| class MenuItemWithIcon extends StatelessWidget { |
| const MenuItemWithIcon(this.icon, this.title, this.subtitle, {Key key}) : super(key: key); |
| |
| final IconData icon; |
| final String title; |
| final String subtitle; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Row( |
| children: <Widget>[ |
| Icon(icon), |
| Padding( |
| padding: const EdgeInsets.only(left: 8.0, right: 8.0), |
| child: Text(title), |
| ), |
| Text(subtitle, style: Theme.of(context).textTheme.caption), |
| ], |
| ); |
| } |
| } |
| |
| class FancyImageItem extends StatelessWidget { |
| const FancyImageItem(this.index, {Key key}) : super(key: key); |
| |
| final int index; |
| |
| @override |
| Widget build(BuildContext context) { |
| return ListBody( |
| children: <Widget>[ |
| UserHeader('Ali Connors $index'), |
| const ItemDescription(), |
| const ItemImageBox(), |
| const InfoBar(), |
| const Padding( |
| padding: EdgeInsets.symmetric(horizontal: 8.0), |
| child: Divider(), |
| ), |
| const IconBar(), |
| const FatDivider(), |
| ], |
| ); |
| } |
| } |
| |
| class FancyGalleryItem extends StatelessWidget { |
| const FancyGalleryItem(this.index, {Key key}) : super(key: key); |
| |
| final int index; |
| @override |
| Widget build(BuildContext context) { |
| return ListBody( |
| children: <Widget>[ |
| const UserHeader('Ali Connors'), |
| ItemGalleryBox(index), |
| const InfoBar(), |
| const Padding( |
| padding: EdgeInsets.symmetric(horizontal: 8.0), |
| child: Divider(), |
| ), |
| const IconBar(), |
| const FatDivider(), |
| ], |
| ); |
| } |
| } |
| |
| class InfoBar extends StatelessWidget { |
| const InfoBar({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: Row( |
| mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| children: <Widget>[ |
| const MiniIconWithText(Icons.thumb_up, '42'), |
| Text('3 Comments', style: Theme.of(context).textTheme.caption), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class IconBar extends StatelessWidget { |
| const IconBar({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.only(left: 16.0, right: 16.0), |
| child: Row( |
| mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| children: const <Widget>[ |
| IconWithText(Icons.thumb_up, 'Like'), |
| IconWithText(Icons.comment, 'Comment'), |
| IconWithText(Icons.share, 'Share'), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class IconWithText extends StatelessWidget { |
| const IconWithText(this.icon, this.title, {Key key}) : super(key: key); |
| |
| final IconData icon; |
| final String title; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Row( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| IconButton( |
| icon: Icon(icon), |
| onPressed: () { print('Pressed $title button'); }, |
| ), |
| Text(title), |
| ], |
| ); |
| } |
| } |
| |
| class MiniIconWithText extends StatelessWidget { |
| const MiniIconWithText(this.icon, this.title, {Key key}) : super(key: key); |
| |
| final IconData icon; |
| final String title; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Row( |
| mainAxisSize: MainAxisSize.min, |
| children: <Widget>[ |
| Padding( |
| padding: const EdgeInsets.only(right: 8.0), |
| child: Container( |
| width: 16.0, |
| height: 16.0, |
| decoration: BoxDecoration( |
| color: Theme.of(context).primaryColor, |
| shape: BoxShape.circle, |
| ), |
| child: Icon(icon, color: Colors.white, size: 12.0), |
| ), |
| ), |
| Text(title, style: Theme.of(context).textTheme.caption), |
| ], |
| ); |
| } |
| } |
| |
| class FatDivider extends StatelessWidget { |
| const FatDivider({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Container( |
| height: 8.0, |
| color: Theme.of(context).dividerColor, |
| ); |
| } |
| } |
| |
| class UserHeader extends StatelessWidget { |
| const UserHeader(this.userName, {Key key}) : super(key: key); |
| |
| final String userName; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: Row( |
| crossAxisAlignment: CrossAxisAlignment.start, |
| children: <Widget>[ |
| const Padding( |
| padding: EdgeInsets.only(right: 8.0), |
| child: Image( |
| image: AssetImage('packages/flutter_gallery_assets/people/square/ali.png'), |
| width: 32.0, |
| height: 32.0, |
| ), |
| ), |
| Expanded( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.start, |
| crossAxisAlignment: CrossAxisAlignment.stretch, |
| children: <Widget>[ |
| RichText(text: TextSpan( |
| style: Theme.of(context).textTheme.bodyText2, |
| children: <TextSpan>[ |
| TextSpan(text: userName, style: const TextStyle(fontWeight: FontWeight.bold)), |
| const TextSpan(text: ' shared a new '), |
| const TextSpan(text: 'photo', style: TextStyle(fontWeight: FontWeight.bold)), |
| ], |
| )), |
| Row( |
| children: <Widget>[ |
| Text('Yesterday at 11:55 • ', style: Theme.of(context).textTheme.caption), |
| Icon(Icons.people, size: 16.0, color: Theme.of(context).textTheme.caption.color), |
| ], |
| ), |
| ], |
| ), |
| ), |
| const TopBarMenu(), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class ItemDescription extends StatelessWidget { |
| const ItemDescription({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return const Padding( |
| padding: EdgeInsets.all(8.0), |
| child: Text('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'), |
| ); |
| } |
| } |
| |
| class ItemImageBox extends StatelessWidget { |
| const ItemImageBox({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: Card( |
| child: Column( |
| crossAxisAlignment: CrossAxisAlignment.stretch, |
| children: <Widget>[ |
| Stack( |
| children: <Widget>[ |
| const SizedBox( |
| height: 230.0, |
| child: Image( |
| image: AssetImage('packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png') |
| ), |
| ), |
| Theme( |
| data: ThemeData.dark(), |
| child: Row( |
| mainAxisAlignment: MainAxisAlignment.end, |
| children: <Widget>[ |
| IconButton( |
| icon: const Icon(Icons.edit), |
| onPressed: () { print('Pressed edit button'); }, |
| ), |
| IconButton( |
| icon: const Icon(Icons.zoom_in), |
| onPressed: () { print('Pressed zoom button'); }, |
| ), |
| ], |
| ), |
| ), |
| Positioned( |
| bottom: 4.0, |
| left: 4.0, |
| child: Container( |
| decoration: BoxDecoration( |
| color: Colors.black54, |
| borderRadius: BorderRadius.circular(2.0), |
| ), |
| padding: const EdgeInsets.all(4.0), |
| child: RichText( |
| text: const TextSpan( |
| style: TextStyle(color: Colors.white), |
| children: <TextSpan>[ |
| TextSpan( |
| text: 'Photo by ' |
| ), |
| TextSpan( |
| style: TextStyle(fontWeight: FontWeight.bold), |
| text: 'Chris Godley', |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| ], |
| ) |
| , |
| Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: Column( |
| crossAxisAlignment: CrossAxisAlignment.stretch, |
| children: <Widget>[ |
| Text('Artisans of Southern India', style: Theme.of(context).textTheme.bodyText1), |
| Text('Silk Spinners', style: Theme.of(context).textTheme.bodyText2), |
| Text('Sivaganga, Tamil Nadu', style: Theme.of(context).textTheme.caption), |
| ], |
| ), |
| ), |
| ], |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class ItemGalleryBox extends StatelessWidget { |
| const ItemGalleryBox(this.index, {Key key}) : super(key: key); |
| |
| final int index; |
| |
| @override |
| Widget build(BuildContext context) { |
| final List<String> tabNames = <String>[ |
| 'A', 'B', 'C', 'D', |
| ]; |
| |
| return SizedBox( |
| height: 200.0, |
| child: DefaultTabController( |
| length: tabNames.length, |
| child: Column( |
| children: <Widget>[ |
| Expanded( |
| child: TabBarView( |
| children: tabNames.map<Widget>((String tabName) { |
| return Container( |
| key: PageStorageKey<String>(tabName), |
| child: Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: Card( |
| child: Column( |
| children: <Widget>[ |
| Expanded( |
| child: Container( |
| color: Theme.of(context).primaryColor, |
| child: Center( |
| child: Text(tabName, style: Theme.of(context).textTheme.headline5.copyWith(color: Colors.white)), |
| ), |
| ), |
| ), |
| Row( |
| children: <Widget>[ |
| IconButton( |
| icon: const Icon(Icons.share), |
| onPressed: () { print('Pressed share'); }, |
| ), |
| IconButton( |
| icon: const Icon(Icons.event), |
| onPressed: () { print('Pressed event'); }, |
| ), |
| Expanded( |
| child: Padding( |
| padding: const EdgeInsets.only(left: 8.0), |
| child: Text('This is item $tabName'), |
| ), |
| ), |
| ], |
| ), |
| ], |
| ), |
| ), |
| ), |
| ); |
| }).toList(), |
| ), |
| ), |
| const TabPageSelector(), |
| ], |
| ), |
| ), |
| ); |
| } |
| } |
| |
| class BottomBar extends StatelessWidget { |
| const BottomBar({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Container( |
| decoration: BoxDecoration( |
| border: Border( |
| top: BorderSide( |
| color: Theme.of(context).dividerColor, |
| width: 1.0, |
| ), |
| ), |
| ), |
| child: Row( |
| mainAxisAlignment: MainAxisAlignment.spaceBetween, |
| children: const <Widget>[ |
| BottomBarButton(Icons.new_releases, 'News'), |
| BottomBarButton(Icons.people, 'Requests'), |
| BottomBarButton(Icons.chat, 'Messenger'), |
| BottomBarButton(Icons.bookmark, 'Bookmark'), |
| BottomBarButton(Icons.alarm, 'Alarm'), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class BottomBarButton extends StatelessWidget { |
| const BottomBarButton(this.icon, this.title, {Key key}) : super(key: key); |
| |
| final IconData icon; |
| final String title; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Padding( |
| padding: const EdgeInsets.all(8.0), |
| child: Column( |
| children: <Widget>[ |
| IconButton( |
| icon: Icon(icon), |
| onPressed: () { print('Pressed: $title'); }, |
| ), |
| Text(title, style: Theme.of(context).textTheme.caption), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class GalleryDrawer extends StatelessWidget { |
| const GalleryDrawer({ Key key }) : super(key: key); |
| |
| void _changeTheme(BuildContext context, bool value) { |
| ComplexLayoutApp.of(context).lightTheme = value; |
| } |
| |
| void _changeScrollMode(BuildContext context, ScrollMode mode) { |
| ComplexLayoutApp.of(context).scrollMode = mode; |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| final ScrollMode currentMode = ComplexLayoutApp.of(context).scrollMode; |
| return Drawer( |
| // Note: for real apps, see the Gallery material Drawer demo. More |
| // typically, a drawer would have a fixed header with a scrolling body |
| // below it. |
| child: ListView( |
| key: const PageStorageKey<String>('gallery-drawer'), |
| padding: EdgeInsets.zero, |
| children: <Widget>[ |
| const FancyDrawerHeader(), |
| ListTile( |
| key: const Key('scroll-switcher'), |
| title: const Text('Scroll Mode'), |
| onTap: () { |
| _changeScrollMode(context, currentMode == ScrollMode.complex ? ScrollMode.tile : ScrollMode.complex); |
| Navigator.pop(context); |
| }, |
| trailing: Text(currentMode == ScrollMode.complex ? 'Tile' : 'Complex'), |
| ), |
| ListTile( |
| leading: const Icon(Icons.brightness_5), |
| title: const Text('Light'), |
| onTap: () { _changeTheme(context, true); }, |
| selected: ComplexLayoutApp.of(context).lightTheme, |
| trailing: Radio<bool>( |
| value: true, |
| groupValue: ComplexLayoutApp.of(context).lightTheme, |
| onChanged: (bool value) { _changeTheme(context, value); }, |
| ), |
| ), |
| ListTile( |
| leading: const Icon(Icons.brightness_7), |
| title: const Text('Dark'), |
| onTap: () { _changeTheme(context, false); }, |
| selected: !ComplexLayoutApp.of(context).lightTheme, |
| trailing: Radio<bool>( |
| value: false, |
| groupValue: ComplexLayoutApp.of(context).lightTheme, |
| onChanged: (bool value) { _changeTheme(context, value); }, |
| ), |
| ), |
| const Divider(), |
| ListTile( |
| leading: const Icon(Icons.hourglass_empty), |
| title: const Text('Animate Slowly'), |
| selected: timeDilation != 1.0, |
| onTap: () { ComplexLayoutApp.of(context).toggleAnimationSpeed(); }, |
| trailing: Checkbox( |
| value: timeDilation != 1.0, |
| onChanged: (bool value) { ComplexLayoutApp.of(context).toggleAnimationSpeed(); }, |
| ), |
| ), |
| ], |
| ), |
| ); |
| } |
| } |
| |
| class FancyDrawerHeader extends StatelessWidget { |
| const FancyDrawerHeader({Key key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) { |
| return Container( |
| color: Colors.purple, |
| height: 200.0, |
| child: const SafeArea( |
| bottom: false, |
| child: Placeholder(), |
| ), |
| ); |
| } |
| } |