// 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:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; enum GridDemoTileStyle { imageOnly, oneLine, twoLine } class Photo { const Photo({ this.assetName }); final String assetName; String get title => 'Safari'; String get caption => 'March 2015'; bool get isValid => assetName != null; } final List<Photo> photos = new List<Photo>.generate(16, (int index) { return const Photo(assetName: 'packages/flutter_gallery_assets/kangaroo_valley_safari.png'); }); class GridDemoPhotoItem extends StatelessWidget { GridDemoPhotoItem({ Key key, this.photo, this.tileStyle }) : super(key: key) { assert(photo != null && photo.isValid); assert(tileStyle != null); } final Photo photo; final GridDemoTileStyle tileStyle; void showPhoto(BuildContext context) { Navigator.push(context, new MaterialPageRoute<Null>( builder: (BuildContext context) { return new Scaffold( appBar: new AppBar( title: new Text(photo.title) ), body: new Material( child: new AssetImage( name: photo.assetName, fit: ImageFit.cover ) ) ); } )); } @override Widget build(BuildContext context) { final Widget image = new GestureDetector( onTap: () { showPhoto(context); }, child: new AssetImage( name: photo.assetName, fit: ImageFit.cover ) ); switch(tileStyle) { case GridDemoTileStyle.imageOnly: return image; case GridDemoTileStyle.oneLine: return new GridTile( header: new GridTileBar( backgroundColor: Colors.black.withAlpha(0x08), leading: new Icon(icon: Icons.info, color: Colors.white70), title: new Text(photo.title) ), child: image ); case GridDemoTileStyle.twoLine: return new GridTile( footer: new GridTileBar( backgroundColor: Colors.black.withAlpha(0x08), title: new Text(photo.title), subtitle: new Text(photo.caption), trailing: new Icon(icon: Icons.info, color: Colors.white70) ), child: image ); } } } class GridListDemoGridDelegate extends FixedColumnCountGridDelegate { GridListDemoGridDelegate({ this.columnCount, double columnSpacing: 0.0, double rowSpacing: 0.0, EdgeInsets padding: EdgeInsets.zero, this.tileHeightFactor: 2.75 }) : super(columnSpacing: columnSpacing, rowSpacing: rowSpacing, padding: padding) { assert(columnCount != null && columnCount >= 0); assert(tileHeightFactor != null && tileHeightFactor > 0.0); } @override final int columnCount; final double tileHeightFactor; @override GridSpecification getGridSpecification(BoxConstraints constraints, int childCount) { assert(constraints.maxWidth < double.INFINITY); assert(constraints.maxHeight < double.INFINITY); return new GridSpecification.fromRegularTiles( tileWidth: math.max(0.0, constraints.maxWidth - padding.horizontal + columnSpacing) / columnCount, tileHeight: constraints.maxHeight / tileHeightFactor, columnCount: columnCount, rowCount: (childCount / columnCount).ceil(), columnSpacing: columnSpacing, rowSpacing: rowSpacing, padding: padding ); } @override bool shouldRelayout(GridListDemoGridDelegate oldDelegate) { return columnCount != oldDelegate.columnCount || tileHeightFactor != oldDelegate.tileHeightFactor || super.shouldRelayout(oldDelegate); } } class GridListDemo extends StatefulWidget { GridListDemo({ Key key }) : super(key: key); @override GridListDemoState createState() => new GridListDemoState(); } class GridListDemoState extends State<GridListDemo> { GridDemoTileStyle tileStyle = GridDemoTileStyle.twoLine; void showTileStyleMenu(BuildContext context) { final List<PopupMenuItem<GridDemoTileStyle>> items = <PopupMenuItem<GridDemoTileStyle>>[ new PopupMenuItem<GridDemoTileStyle>( value: GridDemoTileStyle.imageOnly, child: new Text('Image only') ), new PopupMenuItem<GridDemoTileStyle>( value: GridDemoTileStyle.oneLine, child: new Text('One line') ), new PopupMenuItem<GridDemoTileStyle>( value: GridDemoTileStyle.twoLine, child: new Text('Two line') ) ]; final EdgeInsets padding = MediaQuery.of(context).padding; final ModalPosition position = new ModalPosition( right: padding.right + 16.0, top: padding.top + 16.0 ); showMenu(context: context, position: position, items: items).then((GridDemoTileStyle value) { setState(() { tileStyle = value; }); }); } // When the ScrollableGrid first appears we want the last row to only be // partially visible, to help the user recognize that the grid is scrollable. // The GridListDemoGridDelegate's tileHeightFactor is used for this. @override Widget build(BuildContext context) { final Orientation orientation = MediaQuery.of(context).orientation; return new Scaffold( appBar: new AppBar( title: new Text('Grid List'), actions: <Widget>[ new IconButton( icon: Icons.more_vert, onPressed: () { showTileStyleMenu(context); }, tooltip: 'Show menu' ) ] ), body: new ScrollableGrid( delegate: new GridListDemoGridDelegate( columnCount: (orientation == Orientation.portrait) ? 2 : 3, rowSpacing: 4.0, columnSpacing: 4.0, padding: const EdgeInsets.all(4.0), tileHeightFactor: (orientation == Orientation.portrait) ? 2.75 : 1.75 ), children: photos.map((Photo photo) { return new GridDemoPhotoItem(photo: photo, tileStyle: tileStyle); }) .toList() ) ); } }