Created
February 21, 2020 12:45
-
-
Save rydmike/5e43bde9df72180982398fd4eef1cd87 to your computer and use it in GitHub Desktop.
More GridView Tests and Testing const versus static const class members in Flutter const widgets
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'; | |
const double kMySize = 20.0; | |
const Color kMyGreen = Color(0xFF388E3C); | |
class MyConsts { | |
// Private constructor so the class cannot be externally instantiated | |
// This also hides the code completion, but only when the class | |
// is in another imported file, so it cannot be demoed in DartPad | |
MyConsts._(); | |
static const double mySize = 20.0; | |
static const Color myOrange = Color(0xFFFF7043); | |
} | |
void main() { | |
return 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 Grids', | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData( | |
primarySwatch: Colors.indigo, | |
scaffoldBackgroundColor: Colors.grey[100], | |
buttonTheme: ButtonThemeData( | |
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.indigo), | |
textTheme: ButtonTextTheme.primary, | |
), | |
), | |
home: ExamplesPage(title: 'Flutter Grid and Other Tests'), | |
); | |
} | |
} | |
class ExamplesPage extends StatelessWidget { | |
ExamplesPage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
centerTitle: true, | |
elevation: 0, | |
), | |
body: SingleChildScrollView( | |
padding: EdgeInsets.only(top: 20), | |
child: Row( | |
children: <Widget>[ | |
Expanded( | |
flex: 1, | |
child: Container(), | |
), | |
Container( | |
width: 350, | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: <Widget>[ | |
Text('DEMO - Const versus static const class', | |
style: TextStyle(fontWeight: FontWeight.bold)), | |
Text( | |
'This page shows an example of using const constants versus static ' | |
'const properties'), | |
RaisedButton( | |
child: Text('Flutter consts experiment'), | |
onPressed: () => ConstsDemoPage.show(context, | |
title: 'Flutter Consts Experiment'), | |
), | |
const SizedBox(height: 20), | |
Text('ISSUE - WEB Shadows', | |
style: TextStyle(fontWeight: FontWeight.bold)), | |
Text('This page shows some issues with shadows on WEB, they ' | |
'spread out way too much compared to same shadow on a device.'), | |
RaisedButton( | |
child: Text('Flutter Web shadows issue'), | |
onPressed: () => ShadowsIssuePage.show(context, | |
title: 'Shadows WEB issue'), | |
), | |
const SizedBox(height: 20), | |
Text('EXAMPLE 1 - Simple grid padding case', | |
style: TextStyle(fontWeight: FontWeight.bold)), | |
Text( | |
'Padding on GridView builder is simple, it has a standard ' | |
'padding property, just use it.'), | |
RaisedButton( | |
child: Text('Padding on Gridview builder'), | |
onPressed: () => GridViewBuilderPage.show(context, | |
title: 'Padding a GridView builder'), | |
), | |
const SizedBox(height: 20), | |
Text('EXAMPLE 2 - Padding on SliverGrid', | |
style: TextStyle(fontWeight: FontWeight.bold)), | |
Text( | |
'Padding on SliverGrid is tricker, wrapping entire CustomScrollView ' | |
'with padding FAILS, it will cover shadows!'), | |
RaisedButton( | |
child: Text('Padding Slivers and SliverGrid'), | |
onPressed: () => SliverGridFailPage.show(context, | |
title: 'Padding a SliverGrid FAIL'), | |
), | |
const SizedBox(height: 20), | |
Text('EXAMPLE 3 - Padding SliverGrid with SliverPadding', | |
style: TextStyle(fontWeight: FontWeight.bold)), | |
Text('Padding on SliverGrid is tricker, use SliverPadding!'), | |
RaisedButton( | |
child: Text('Padding Slivers and SliverGrid'), | |
onPressed: () => SliverGridPage.show(context, | |
title: 'Padding a SliverGrid OK'), | |
), | |
const SizedBox(height: 20), | |
Text('EXAMPLE 4 - SliverGrid fancy pants', | |
style: TextStyle(fontWeight: FontWeight.bold)), | |
Text('Padding a SliverGrid works on extend behind ' | |
'AppBar and fancy transparent gradient AppBar too'), | |
RaisedButton( | |
child: Text('Padding fancy Slivers and SliverGrid'), | |
onPressed: () => SliverGridExtPage.show(context, | |
title: 'Padding a SliverGrid OK'), | |
), | |
], | |
), | |
), | |
Expanded( | |
flex: 1, | |
child: Container(), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class ConstsDemoPage extends StatelessWidget { | |
const ConstsDemoPage({Key key, this.title}) : super(key: key); | |
final String title; | |
static Future<void> show(BuildContext context, {String title}) async { | |
await Navigator.of(context).push( | |
MaterialPageRoute(builder: (context) => ConstsDemoPage(title: title))); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
centerTitle: true, | |
elevation: 0, | |
), | |
body: SingleChildScrollView( | |
child: Padding( | |
padding: const EdgeInsets.all(kMySize), | |
child: Column( | |
children: <Widget>[ | |
const Text('I am const sized text', | |
style: TextStyle(fontSize: kMySize)), | |
const SizedBox(height: MyConsts.mySize), | |
const Text('I am static const sized text', | |
style: TextStyle(fontSize: MyConsts.mySize)), | |
const SizedBox(height: kMySize), | |
const Text( | |
'The static const and plain const worked fine with both const Text and SizedBox', | |
style: TextStyle(fontSize: 16)), | |
const SizedBox(height: kMySize), | |
Text('Trying const Cards, with const color', | |
style: TextStyle(fontSize: 16)), | |
const Card( | |
elevation: 3, | |
margin: EdgeInsets.all(0), | |
color: kMyGreen, | |
child: const SizedBox( | |
height: 100, | |
width: double.infinity, | |
), | |
), | |
const SizedBox(height: MyConsts.mySize), | |
Text('Trying const Cards, with static const color', | |
style: TextStyle(fontSize: 16)), | |
const Card( | |
elevation: 3, | |
margin: EdgeInsets.all(0), | |
color: MyConsts.myOrange, | |
child: const SizedBox( | |
height: 100, | |
width: double.infinity, | |
), | |
), | |
const SizedBox(height: MyConsts.mySize), | |
Text('Trying const Cards, with Material color', | |
style: TextStyle(fontSize: 16)), | |
const Card( | |
elevation: 3, | |
margin: EdgeInsets.all(0), | |
color: Colors.blueAccent, | |
child: const SizedBox( | |
height: 100, | |
width: double.infinity, | |
), | |
), | |
const SizedBox(height: 20), | |
const SelectableText( | |
'All these examples worked fine. By the way, this text is selectable text. ' | |
'The other texts above are not selectable. ' | |
'You can almost also select this text on web, but still cannot copy ' | |
'it on WEB, bummer.'), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
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 ShadowsIssuePage extends StatelessWidget { | |
const ShadowsIssuePage({Key key, this.title}) : super(key: key); | |
final String title; | |
static Future<void> show(BuildContext context, {String title}) async { | |
await Navigator.of(context).push(MaterialPageRoute( | |
builder: (context) => ShadowsIssuePage(title: title))); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
), | |
body: SingleChildScrollView( | |
child: Padding( | |
padding: const EdgeInsets.fromLTRB(20, 30, 20, 50), | |
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 GridViewBuilderPage extends StatelessWidget { | |
const GridViewBuilderPage({Key key, this.title}) : super(key: key); | |
final String title; | |
static Future<void> show(BuildContext context, {String title}) async { | |
await Navigator.of(context).push(MaterialPageRoute( | |
builder: (context) => GridViewBuilderPage(title: title))); | |
} | |
@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 Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
centerTitle: true, | |
elevation: 0, | |
), | |
body: Scrollbar( | |
child: GridView.builder( | |
padding: EdgeInsets.all(10), | |
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, this.title}) : super(key: key); | |
final String title; | |
static Future<void> show(BuildContext context, {String title}) async { | |
await Navigator.of(context).push(MaterialPageRoute( | |
builder: (context) => SliverGridFailPage(title: title))); | |
} | |
@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 Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
centerTitle: true, | |
elevation: 0, | |
), | |
body: Scrollbar( | |
child: Padding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), | |
child: CustomScrollView( | |
slivers: <Widget>[ | |
SliverList( | |
delegate: SliverChildListDelegate([ | |
SizedBox(height: 20), | |
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, | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class SliverGridPage extends StatelessWidget { | |
const SliverGridPage({Key key, this.title}) : super(key: key); | |
final String title; | |
static Future<void> show(BuildContext context, {String title}) async { | |
await Navigator.of(context).push( | |
MaterialPageRoute(builder: (context) => SliverGridPage(title: title))); | |
} | |
@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 Scaffold( | |
appBar: AppBar( | |
title: Text(title), | |
centerTitle: true, | |
elevation: 0, | |
), | |
body: Scrollbar( | |
child: CustomScrollView( | |
slivers: <Widget>[ | |
SliverPadding( | |
padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), | |
sliver: SliverList( | |
delegate: SliverChildListDelegate([ | |
SizedBox(height: 20), | |
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. To get the ' | |
'Padding effect, we must wrap each Sliver in a ' | |
'SliverPadding!'), | |
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, | |
), | |
), | |
) | |
], | |
), | |
), | |
); | |
} | |
} | |
// ***************************************************************************** | |
class SliverGridExtPage extends StatelessWidget { | |
const SliverGridExtPage({Key key, this.title}) : super(key: key); | |
final String title; | |
static Future<void> show(BuildContext context, {String title}) async { | |
await Navigator.of(context).push(MaterialPageRoute( | |
builder: (context) => SliverGridExtPage(title: title))); | |
} | |
@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 Scaffold( | |
extendBodyBehindAppBar: true, | |
extendBody: true, | |
appBar: AppBar( | |
title: Text(title), | |
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: Scrollbar( | |
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: 10 + | |
MediaQuery.of(context).padding.top + | |
kToolbarHeight), | |
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 ' | |
'StaggaredGrid and its SliverStaggaredGrid but that cannot be ' | |
'shown in DartPad, but here is an another example showing it: '), | |
SizedBox(height: 5), | |
SelectableText( | |
'https://gist.github.com/rydmike/5997737351268ad08e35e9f406e73f39'), | |
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, | |
), | |
), | |
) | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
As a result of the discussions on this thread I added to my SliverGrid experiments a test to see how const versus static const in classes behave when used for const Widgets. It is the first button in this updated experiment/test. You can run it in DartPad here