Skip to content

Instantly share code, notes, and snippets.

@av
Last active October 18, 2019 14:34
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 av/75485af5b21881ac89d99c78a53c8a6f to your computer and use it in GitHub Desktop.
Save av/75485af5b21881ac89d99c78a53c8a6f to your computer and use it in GitHub Desktop.
Flutter: magic, initial prototype
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
/// Represents some additional [Color]s to be used
/// across the application
class Palette {
static final background = Color(0xff202030);
static final background700 = Color(0xff303040);
static final accent = Colors.deepPurpleAccent.shade200;
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
var textTheme = Typography.whiteMountainView.copyWith(
subtitle: Typography.whiteMountainView.subtitle.copyWith(
fontWeight: FontWeight.w900,
),
title: Typography.whiteMountainView.title.copyWith(
fontWeight: FontWeight.w900,
letterSpacing: -1,
fontSize: 28,
color: Colors.white,
),
display1: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w600,
letterSpacing: -2,
),
caption: Typography.whiteMountainView.caption.copyWith(
fontStyle: FontStyle.italic,
),
);
return MaterialApp(
theme: ThemeData.dark().copyWith(
textTheme: textTheme,
accentColor: Palette.accent,
scaffoldBackgroundColor: Palette.background,
backgroundColor: Palette.background,
),
home: Root(),
);
}
}
/// Application root container
class Root extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
// Black backdrop when outscrolling
// the view to the top
Container(
color: Colors.black,
height: 350,
),
SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black,
Palette.background,
],
stops: [
0,
.2
]),
),
child: Column(
children: <Widget>[
Toolbar(),
Categories(),
Padding(
padding: const EdgeInsets.all(32.0),
child: Quote(
body:
'It\'s still magic even if you know how it\'s done. 🔮',
author: 'Terry Pratchett, A Hat Full of Sky',
),
),
ProductSetout(products: firstRowProducts.values.toList()),
ProductSetout(products: secondRowProducts.values.toList()),
Container(
height: 80,
)
],
),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 80,
child: Cart(),
)
],
),
);
}
}
class Quote extends StatelessWidget {
final String body;
final String author;
const Quote({this.body, this.author});
@override
Widget build(BuildContext context) {
var textTheme = Theme.of(context).textTheme;
return Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(
Icons.format_quote,
size: 40.0,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'$body',
style: textTheme.display1,
),
Container(
height: 8.0,
),
Text(
author,
style: textTheme.caption,
)
],
),
),
],
),
);
}
}
class Categories extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
height: 20,
child: ListView(
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
children: <Widget>[
Category(name: 'All', selected: true),
Category(name: 'Cards'),
Category(name: 'Coins'),
Category(name: 'Mentalism'),
Category(name: 'Comedy'),
],
),
);
}
}
class Category extends StatelessWidget {
final String name;
final bool selected;
Category({
this.name,
this.selected = false,
});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 32.0),
padding: selected ? const EdgeInsets.symmetric(horizontal: 8.0) : null,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
color: selected ? Colors.white : Colors.transparent,
),
child: Text(
name.toUpperCase(),
style: Theme.of(context).textTheme.subtitle.copyWith(
height: 1.3, color: selected ? Palette.background : Colors.white),
),
);
}
}
class Toolbar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 48.0, horizontal: 32.0),
child: Row(children: <Widget>[
Icon(Icons.menu),
flexy(),
Text('Pure Magic Inc.'.toUpperCase(),
style: Theme.of(context).textTheme.title)
]),
);
}
}
class ProductSetout extends StatefulWidget {
final List<Product> products;
const ProductSetout({this.products});
@override
_ProductSetoutState createState() => _ProductSetoutState();
}
class _ProductSetoutState extends State<ProductSetout> {
final Random rnd = Random();
ScrollController controller;
@override
void initState() {
controller = ScrollController(
initialScrollOffset: rnd.nextInt(500).toDouble(),
);
super.initState();
}
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: BoxConstraints.tightFor(height: 380),
child: ListView(
padding: const EdgeInsets.all(16.0),
controller: controller,
scrollDirection: Axis.horizontal,
physics: BouncingScrollPhysics(),
children: widget.products
.map<Widget>((product) => ProductCard(product: product))
.toList(),
),
);
}
}
class ProductCard extends StatelessWidget {
final Product product;
ProductCard({this.product});
@override
Widget build(BuildContext context) {
return Container(
width: 240,
margin: const EdgeInsets.all(16.0),
decoration: BoxDecoration(
color: Palette.background700,
borderRadius: BorderRadius.circular(32.0),
boxShadow: [
BoxShadow(
blurRadius: 16,
offset: Offset(0, 16),
color: Colors.black26,
)
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(32.0),
child: Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
foregroundDecoration: BoxDecoration(
color: Colors.grey.withOpacity(.5),
backgroundBlendMode: BlendMode.saturation,
),
child: Image.network(
product.photo,
fit: BoxFit.cover,
),
),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.2, 0.8],
colors: [Colors.transparent, Palette.background700],
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
flexy(),
Text(
product.name,
style: Theme.of(context).textTheme.title,
),
Container(
height: 8.0,
),
Text(
product.description,
style: Theme.of(context).textTheme.caption,
),
Container(
height: 16.0,
),
OutlineButton(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('\$${product.price}',
style: Theme.of(context).textTheme.title),
],
),
),
onPressed: () {
print('Adding $product to cart!');
},
)
],
),
)
],
),
),
);
}
}
class OutlineButton extends StatelessWidget {
final Widget child;
final void Function() onPressed;
const OutlineButton({
@required this.child,
@required this.onPressed,
});
@override
Widget build(BuildContext context) {
final borderRadius = BorderRadius.circular(32.0);
return ClipRRect(
borderRadius: borderRadius,
child: RawMaterialButton(
onPressed: onPressed,
child: Container(
child: child,
decoration: BoxDecoration(
borderRadius: borderRadius,
border: Border.all(
color: Colors.white,
width: 3.0,
),
)
),
),
);
}
}
class Cart extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32.0),
topRight: Radius.circular(32.0),
),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 10,
sigmaY: 10,
),
child: Container(
decoration: BoxDecoration(
color: Colors.white24,
),
padding: const EdgeInsets.all(16.0),
child: Row(
children: <Widget>[
flexy(),
OutlineButton(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 16.0),
child: Text(
'Checkout',
style: Theme.of(context).textTheme.title,
),
),
onPressed: () {
print('Checking out all items in the cart!');
},
)
],
),
),
),
),
);
}
}
Widget flexy() {
return Expanded(
child: Container(),
);
}
class Product {
final String name;
final String description;
final String photo;
final double price;
Product({this.name, this.description, this.photo, this.price});
@override
String toString() => 'Product($name)';
}
final Map<String, Product> firstRowProducts = {
'vanishingCoins': Product(
name: 'Bitcoin',
description: 'Make money instantly vanish in your hand!',
photo:
'https://cdn.pixabay.com/photo/2017/02/11/10/24/bitcoin-2057405__340.jpg',
price: 59.99,
),
'diamondsCard': Product(
name: 'Hand of midas',
description: 'Do not pet your dog with it!',
photo:
'https://www.smashinglists.com/wp-content/uploads/2010/07/Hand-of-Midas1-600x399.jpg',
price: 99.99,
),
'flyingCarpter': Product(
name: 'Flying Carpet',
description: 'You best last mile transport. Works for any other mile too!',
price: 499.99,
photo:
'https://www.smashinglists.com/wp-content/uploads/2010/07/flying-carpet-600x330.jpg',
),
};
final Map<String, Product> secondRowProducts = {
'magicLamp': Product(
name: 'Magic Lamp',
description: 'Rub with caution, do not shake.',
price: 69.99,
photo:
'https://www.smashinglists.com/wp-content/uploads/2010/07/magic-lamp-600x416.jpg',
),
'theRing': Product(
name: 'The Ring',
description:
'Just a ring. Nothing special about it. Just move on. Do not look. Skip it.',
price: 89.99,
photo:
'https://www.smashinglists.com/wp-content/uploads/2010/07/onering-600x431.jpg',
),
'philosophersStone': Product(
name: 'Philosopher\'s stone',
description:
'Philosopher didn\'t tell what the stone is for. Should be useful.',
price: 129.99,
photo:
'https://static1.therichestimages.com/wordpress/wp-content/uploads/2016/11/pottermore.jpg?q=50&fit=crop&w=963&h=617'),
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment