Last active
June 15, 2020 22:03
-
-
Save rydmike/8becd0bf40f66a581e4848b85a51fefe to your computer and use it in GitHub Desktop.
PageView with Scrolling Lists and Grids
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
// On a device this [setSystemUIOverlayStyle] call will make your AppBar cool | |
// on Android. It also helps with the AppBar effect shown just for fun in | |
// Example 4 by also making the transparent gradient AppBar | |
// visible on the top system status icons and it also in the other examples | |
// makes it so that the AppBar and status icons area always uses the | |
// same color as the one used in Flutter's AppBar | |
// and not standard Android two toned one, so it looks like an iPhone :) | |
SystemChrome.setSystemUIOverlayStyle( | |
SystemUiOverlayStyle( | |
systemNavigationBarColor: Colors.grey[100], | |
statusBarColor: Colors.transparent, | |
statusBarIconBrightness: Brightness.light, | |
systemNavigationBarIconBrightness: Brightness.dark, | |
), | |
); | |
return MaterialApp( | |
title: 'Flutter Tutorial', | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: HomePage()); | |
} | |
} | |
class HomePage extends StatefulWidget { | |
@override | |
_HomePageState createState() => _HomePageState(); | |
} | |
class _HomePageState extends State<HomePage> { | |
int currentIndex = 0; | |
PageController pageController; | |
@override | |
void initState() { | |
super.initState(); | |
pageController = PageController(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return WillPopScope( | |
onWillPop: () { | |
return; | |
}, | |
child: Scaffold( | |
extendBodyBehindAppBar: true, | |
extendBody: true, | |
appBar: AppBar( | |
title: Text('PageView Demo'), | |
centerTitle: true, | |
elevation: 0, | |
backgroundColor: Colors.transparent, | |
// Fancy gradient partially transparent AppBar | |
flexibleSpace: Container( | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
begin: Alignment.topLeft, | |
end: Alignment.topRight, | |
colors: [ | |
Colors.indigo, | |
Colors.indigo.withOpacity(0.6), | |
], | |
), | |
), | |
child: null, | |
), | |
), | |
body: PageView( | |
controller: pageController, | |
children: <Widget>[ | |
Container(child: ShadowsPage()), | |
Container(child: ListViewPage()), | |
Container(child: GridViewPage()), | |
Container(child: SliverGridFailPage()), | |
Container(child: SliverGridExtPage()), | |
], | |
onPageChanged: (int index) { | |
setState(() { | |
currentIndex = index; | |
}); | |
}), | |
bottomNavigationBar: bottomItems())); | |
} | |
BottomNavigationBar bottomItems() { | |
return BottomNavigationBar( | |
selectedItemColor: Colors.blue, | |
onTap: (int index) { | |
setState(() { | |
currentIndex = index; | |
}); | |
pageController.animateToPage( | |
index, | |
duration: Duration( | |
milliseconds: 200, | |
), | |
curve: Curves.easeIn, | |
); | |
}, | |
backgroundColor: Theme.of(context).cardColor.withOpacity(.9), | |
currentIndex: currentIndex, | |
type: BottomNavigationBarType.fixed, | |
items: <BottomNavigationBarItem>[ | |
BottomNavigationBarItem( | |
icon: Icon(Icons.featured_play_list), title: Text('Shadows')), | |
BottomNavigationBarItem(icon: Icon(Icons.list), title: Text('List')), | |
BottomNavigationBarItem( | |
icon: Icon( | |
Icons.grid_on, | |
), | |
title: Text('Grid')), | |
BottomNavigationBarItem( | |
icon: Icon(Icons.border_right), title: Text('SliverFail')), | |
BottomNavigationBarItem( | |
icon: Icon(Icons.border_all), title: Text('SliverOK')) | |
]); | |
} | |
} | |
// ***************************************************************************** | |
class GridItem extends StatelessWidget { | |
const GridItem({Key key, this.title, this.color, this.height, this.bodyText}) | |
: super(key: key); | |
final String title; | |
final Color color; | |
final double height; | |
final String bodyText; | |
@override | |
Widget build(BuildContext context) { | |
return Container( | |
color: color, | |
padding: const EdgeInsets.all(10), | |
child: Column( | |
children: <Widget>[ | |
Text( | |
title, | |
style: TextStyle( | |
color: Colors.white, | |
fontSize: 18, | |
), | |
), | |
if (height != null && height > 0) SizedBox(height: height), | |
if (height != null && height > 0) | |
Text(bodyText, | |
style: TextStyle( | |
color: Colors.white, fontWeight: FontWeight.bold)), | |
], | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class ShadowsPage extends StatelessWidget { | |
const ShadowsPage({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return SingleChildScrollView( | |
key: const PageStorageKey<String>('Shadows'), | |
child: Padding( | |
padding: EdgeInsets.fromLTRB( | |
15, | |
15 + MediaQuery.of(context).padding.top, | |
15, | |
15 + MediaQuery.of(context).padding.bottom), | |
child: Column( | |
children: <Widget>[ | |
Text('Shadows on Web look incorrect', | |
style: TextStyle(fontSize: 25)), | |
SizedBox(height: 10), | |
Text('Shadows bleed too much compared to device rendering', | |
style: TextStyle(fontSize: 16)), | |
const SizedBox(height: 20), | |
Card( | |
elevation: 0, | |
margin: EdgeInsets.all(0), | |
color: Colors.indigo[100], | |
child: Container( | |
child: Center(child: Text('Material Card with elevation 0')), | |
height: 100, | |
), | |
), | |
const SizedBox(height: 20), | |
Card( | |
elevation: 5, | |
margin: EdgeInsets.all(0), | |
color: Colors.indigo[100], | |
child: Container( | |
child: Center(child: Text('Material Card with elevation 5')), | |
height: 100, | |
), | |
), | |
const SizedBox(height: 20), | |
Card( | |
elevation: 10, | |
margin: EdgeInsets.all(0), | |
color: Colors.indigo[100], | |
child: Container( | |
child: Center(child: Text('Material Card with elevation 10')), | |
height: 100, | |
), | |
), | |
const SizedBox(height: 20), | |
Container( | |
decoration: BoxDecoration( | |
borderRadius: BorderRadius.circular(4), | |
boxShadow: <BoxShadow>[ | |
BoxShadow( | |
color: Colors.indigo.withOpacity(0.7), | |
offset: Offset(3, 4), | |
blurRadius: 6, | |
spreadRadius: 3, | |
), | |
], | |
), | |
child: Container( | |
decoration: BoxDecoration( | |
borderRadius: BorderRadius.circular(4), | |
color: Colors.indigo[100], | |
), | |
child: Center(child: Text('Container with custom BoxShadow')), | |
height: 100, | |
), | |
), | |
const SizedBox(height: 20), | |
SelectableText( | |
'The fonts on WEB are also fuzzier, like too much antialias'), | |
SelectableText( | |
'The shadow issue is reported here: https://github.com/flutter/flutter/issues/32215 '), | |
], | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class ListViewPage extends StatelessWidget { | |
const ListViewPage({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
var _gridItems = List<GridItem>.generate(400, (index) { | |
return GridItem( | |
title: 'List item nr ${index + 1}', | |
color: Colors.primaries[index % Colors.primaries.length][800]); | |
}); | |
return Scrollbar( | |
key: const PageStorageKey<String>('List'), | |
child: ListView.builder( | |
padding: EdgeInsets.fromLTRB( | |
15, | |
15 + MediaQuery.of(context).padding.top, | |
15, | |
15 + MediaQuery.of(context).padding.bottom), | |
itemCount: _gridItems.length, | |
itemBuilder: (context, index) => Card( | |
elevation: 6, | |
child: _gridItems[index], | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class GridViewPage extends StatelessWidget { | |
const GridViewPage({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
var _gridItems = List<GridItem>.generate(400, (index) { | |
return GridItem( | |
title: 'Tile nr ${index + 1}', | |
color: Colors.primaries[index % Colors.primaries.length][800]); | |
}); | |
return Scrollbar( | |
key: const PageStorageKey<String>('Grid'), | |
child: GridView.builder( | |
padding: EdgeInsets.fromLTRB( | |
15, | |
15 + MediaQuery.of(context).padding.top, | |
15, | |
15 + MediaQuery.of(context).padding.bottom), | |
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( | |
crossAxisCount: 3, | |
mainAxisSpacing: 10, | |
crossAxisSpacing: 10, | |
childAspectRatio: 2, | |
), | |
itemCount: _gridItems.length, | |
itemBuilder: (context, index) => Card( | |
elevation: 6, | |
child: _gridItems[index], | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class SliverGridFailPage extends StatelessWidget { | |
const SliverGridFailPage({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
var _gridItems = List<GridItem>.generate(400, (index) { | |
return GridItem( | |
title: 'Tile nr ${index + 1}', | |
color: Colors.primaries[index % Colors.primaries.length][800]); | |
}); | |
return Scrollbar( | |
key: const PageStorageKey<String>('SliverFail'), | |
child: Padding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), | |
child: CustomScrollView( | |
slivers: <Widget>[ | |
SliverList( | |
delegate: SliverChildListDelegate([ | |
SizedBox(height: 15 + MediaQuery.of(context).padding.top), | |
Text( | |
'SliverGrid Padding FAIL', | |
style: Theme.of(context).textTheme.headline4, | |
), | |
Text('Texts and header in own SliverList so that we can ' | |
'scroll them with the scrolling grid. If we WRAP the ' | |
'CustomScrollView in a Padding, the result is uggly = FAIL! ' | |
'The ELEVATION shadows will be covered by the padding!'), | |
SizedBox(height: 20), | |
]), | |
), | |
SliverGrid( | |
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( | |
crossAxisCount: 3, | |
mainAxisSpacing: 15, | |
crossAxisSpacing: 15, | |
childAspectRatio: 2, | |
), | |
delegate: SliverChildBuilderDelegate( | |
(ctx, index) { | |
return Card( | |
elevation: 6, | |
child: _gridItems[index], | |
); | |
}, | |
childCount: _gridItems.length, | |
), | |
), | |
SliverList( | |
delegate: SliverChildListDelegate([ | |
SizedBox(height: 15 + MediaQuery.of(context).padding.bottom), | |
]), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class SliverGridExtPage extends StatelessWidget { | |
const SliverGridExtPage({Key key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
var _gridItems = List<GridItem>.generate(200, (index) { | |
return GridItem( | |
title: 'Tile nr ${index + 1}', | |
color: Colors.primaries[index % Colors.primaries.length][800]); | |
}); | |
return Scrollbar( | |
key: const PageStorageKey<String>('SliverOK'), | |
child: CustomScrollView( | |
slivers: <Widget>[ | |
SliverPadding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), | |
sliver: SliverList( | |
delegate: SliverChildListDelegate([ | |
// We need to add back the padding we removed by allowing scrolling under the toolbar | |
SizedBox(height: 15 + MediaQuery.of(context).padding.top), | |
Text( | |
'SliverGrid OK Padding', | |
style: Theme.of(context).textTheme.headline4, | |
), | |
Text('Texts and header in own SliverList so that we can ' | |
'scroll them with the scrolling grid. Here with scroll ' | |
'behind a fancy gradient transparent AppBar!'), | |
SizedBox(height: 10), | |
Text('SliverPadding also works with package '), | |
SizedBox(height: 20), | |
]), | |
), | |
), | |
SliverPadding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), | |
sliver: SliverGrid( | |
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( | |
crossAxisCount: 3, | |
mainAxisSpacing: 15, | |
crossAxisSpacing: 15, | |
childAspectRatio: 2, | |
), | |
delegate: SliverChildBuilderDelegate( | |
(ctx, index) { | |
return Card( | |
elevation: 6, | |
child: _gridItems[index], | |
); | |
}, | |
childCount: _gridItems.length, | |
), | |
), | |
), | |
SliverPadding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), | |
sliver: SliverList( | |
delegate: SliverChildListDelegate([ | |
SizedBox(height: 15 + MediaQuery.of(context).padding.bottom), | |
]), | |
), | |
), | |
], | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment