Skip to content

Instantly share code, notes, and snippets.

@rydmike
Last active June 15, 2020 22:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rydmike/8becd0bf40f66a581e4848b85a51fefe to your computer and use it in GitHub Desktop.
Save rydmike/8becd0bf40f66a581e4848b85a51fefe to your computer and use it in GitHub Desktop.
PageView with Scrolling Lists and Grids
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