Skip to content

Instantly share code, notes, and snippets.

@Zfinix
Created November 13, 2020 09:15
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zfinix/c4a9836ac33ca9c17cdd2af520d1fab4 to your computer and use it in GitHub Desktop.
Save Zfinix/c4a9836ac33ca9c17cdd2af520d1fab4 to your computer and use it in GitHub Desktop.
import 'dart:convert';
import 'dart:math' as math;
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:image/image.dart' as image;
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: Frame(app: TPaint()),
builder: (v, k) => Scaffold(body: k),
),
);
}
final key = List.generate(9, (index) => GlobalKey<SnappableState>());
class TPaint extends StatefulWidget {
@override
_TPaintState createState() => _TPaintState();
}
class _TPaintState extends State<TPaint> with SingleTickerProviderStateMixin {
final red = Color(0xffff0032);
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
physics: BouncingScrollPhysics(),
children: <Widget>[
Padding(
padding: const EdgeInsets.all(18.0)
.add(EdgeInsets.symmetric(horizontal: 20)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Snappable(
key: key[0],
child: Row(
children: <Widget>[
InkResponse(
child: Image.memory(
base64.decode(menu),
scale: 2.5,
),
onTap: () {},
),
Spacer(),
CircleAvatar(
backgroundImage: NetworkImage(
'https://pbs.twimg.com/profile_images/1325707629113565185/Yqn23Vv7_400x400.jpg'),
)
],
),
),
const YMargin(38),
Snappable(
key: key[1],
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Hi Chizi,\nhave a good day!',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 27,
),
)
],
),
),
const YMargin(10),
Snappable(
key: key[2],
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'You have two (2) shipments\nplanned for today.',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.grey,
fontSize: 14,
height: 1.4),
)
],
),
),
],
),
),
const YMargin(15),
Snappable(
key: key[3],
child: Stack(
children: <Widget>[
Container(
height: 189,
margin: EdgeInsets.symmetric(vertical: 30, horizontal: 45),
width: context.screenWidth(),
decoration: BoxDecoration(
color: red,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.redAccent.withOpacity(0.9),
blurRadius: 50,
offset: Offset(4, 20),
spreadRadius: -11),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const YMargin(239),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 6.4,
height: 6.4,
margin: EdgeInsets.all(2),
decoration: BoxDecoration(
color: red,
borderRadius: BorderRadius.circular(40),
)),
for (var i = 0; i < 3; i++)
Container(
width: 6,
height: 6,
margin: EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.grey[400],
borderRadius: BorderRadius.circular(40),
)),
],
)
],
),
Container(
height: 200,
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
width: context.screenWidth(),
decoration: BoxDecoration(
color: red,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 50,
offset: Offset(4, 20),
spreadRadius: -11),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0, vertical: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Shipment Status',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white,
fontSize: 14,
height: 1.4),
),
Spacer(),
Text(
'#DSX2346',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white,
fontSize: 14,
height: 1.4),
)
],
),
const YMargin(17),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 50,
width: 50,
child: Image.memory(base64.decode(box)),
),
const XMargin(20),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Your shipment is being delivered',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white,
fontSize: 15),
),
Text(
'Last Update: 23 min ago.',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white70,
fontSize: 12),
)
],
),
],
),
const YMargin(20),
Container(
height: 4,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Theme(
data: ThemeData(
primaryColor: Color(0xfff6c833),
accentColor: Color(0xfff6c833)),
child: LinearProgressIndicator(
value: 0.6,
backgroundColor: Color(0xffe0002c),
),
),
),
),
const YMargin(20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Track Your Package',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.white,
fontSize: 15,
height: 1.4),
),
Spacer(),
Icon(
Icons.arrow_forward,
color: Colors.white,
size: 19,
)
],
),
],
),
),
),
],
),
),
const YMargin(40),
Snappable(
key: key[4],
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30, vertical: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Text(
'Your Activity',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.grey[800],
fontSize: 18,
height: 1.4),
),
),
const YMargin(9),
Container(
height: 2,
width: 115,
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.circular(20),
)),
],
),
Spacer(),
Row(
children: <Widget>[
Text(
'Rating',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.grey[900],
fontSize: 18,
height: 1.4),
),
const XMargin(10),
Text(
'4.8',
style: TextStyle(
fontWeight: FontWeight.w800,
color: red,
fontSize: 18,
height: 1.4),
),
],
),
],
),
),
),
Snappable(
key: key[5],
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 30, vertical: 5),
title: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(
'https://pbs.twimg.com/profile_images/1322301184510746625/ZD-vSRZj_400x400.jpg',
),
),
const XMargin(11),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Yesterday',
style: TextStyle(
fontWeight: FontWeight.w200,
color: Colors.grey,
fontSize: 13,
height: 1.4),
),
const YMargin(5),
RichText(
text: TextSpan(
text: 'You have recieved a new rating from\n',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.black,
fontSize: 15),
children: <TextSpan>[
TextSpan(
text: 'Maryann Onuoha',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 15),
)
]),
),
],
),
],
),
trailing: Icon(
Icons.arrow_forward_ios,
size: 14,
),
),
),
Snappable(
key: key[6],
child: ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 30, vertical: 5)
.add(EdgeInsets.only(bottom: 10)),
title: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(
'https://pbs.twimg.com/profile_images/1319013042378756097/2ymZwpLo_400x400.jpg'),
),
const XMargin(11),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'January 4',
style: TextStyle(
fontWeight: FontWeight.w200,
color: Colors.grey,
fontSize: 13,
height: 1.4),
),
const YMargin(5),
Container(
child: RichText(
text: TextSpan(
text: 'Atarah, the Figmama.\n',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 15),
children: <TextSpan>[
TextSpan(
text: 'accepted your shipment request.',
style: TextStyle(
fontWeight: FontWeight.w400,
color: Colors.black,
fontSize: 15),
)
]),
),
)
],
),
],
),
trailing: Icon(
Icons.arrow_forward_ios,
size: 14,
),
),
)
],
),
);
}
}
//https://github.com/MarcinusX/snappable
class Snappable extends StatefulWidget {
/// Widget to be snapped
final Widget child;
/// Direction and range of snap effect
/// (Where and how far will particles go)
final Offset offset;
/// Duration of whole snap animation
final Duration duration;
/// How much can particle be randomized,
/// For example if [offset] is (100, 100) and [randomDislocationOffset] is (10,10),
/// Each layer can be moved to maximum between 90 and 110.
final Offset randomDislocationOffset;
/// Number of layers of images,
/// The more of them the better effect but the more heavy it is for CPU
final int numberOfBuckets;
/// Quick helper to snap widgets when touched
/// If true wraps the widget in [GestureDetector] and starts [snap] when tapped
/// Defaults to false
final bool snapOnTap;
/// Function that gets called when snap ends
final VoidCallback onSnapped;
const Snappable({
Key key,
@required this.child,
this.offset = const Offset(64, -32),
this.duration = const Duration(milliseconds: 5000),
this.randomDislocationOffset = const Offset(64, 32),
this.numberOfBuckets = 16,
this.snapOnTap = false,
this.onSnapped,
}) : super(key: key);
@override
SnappableState createState() => SnappableState();
}
class SnappableState extends State<Snappable>
with SingleTickerProviderStateMixin {
static const double _singleLayerAnimationLength = 0.6;
static const double _lastLayerAnimationStart =
1 - _singleLayerAnimationLength;
bool get isGone => _animationController.isCompleted;
/// Main snap effect controller
AnimationController _animationController;
/// Key to get image of a [widget.child]
GlobalKey _globalKey = GlobalKey();
/// Layers of image
List<Uint8List> _layers;
/// Values from -1 to 1 to dislocate the layers a bit
List<double> _randoms;
/// Size of child widget
Size size;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: widget.duration,
);
if (widget.onSnapped != null) {
_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) widget.onSnapped();
});
}
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.snapOnTap ? () => isGone ? reset() : snap() : null,
child: Stack(
children: <Widget>[
if (_layers != null) ..._layers.map(_imageToWidget),
AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
return _animationController.isDismissed ? child : Container();
},
child: RepaintBoundary(
key: _globalKey,
child: widget.child,
),
)
],
),
);
}
/// I am... INEVITABLE ~Thanos
Future<void> snap() async {
//get image from child
final fullImage = await _getImageFromWidget();
//create an image for every bucket
List<image.Image> _images = List<image.Image>.generate(
widget.numberOfBuckets,
(i) => image.Image(fullImage.width, fullImage.height),
);
//for every line of pixels
for (int y = 0; y < fullImage.height; y++) {
//generate weight list of probabilities determining
//to which bucket should given pixels go
List<int> weights = List.generate(
widget.numberOfBuckets,
(bucket) => _gauss(
y / fullImage.height,
bucket / widget.numberOfBuckets,
),
);
int sumOfWeights = weights.fold(0, (sum, el) => sum + el);
//for every pixel in a line
for (int x = 0; x < fullImage.width; x++) {
//get the pixel from fullImage
int pixel = fullImage.getPixel(x, y);
//choose a bucket for a pixel
int imageIndex = _pickABucket(weights, sumOfWeights);
//set the pixel from chosen bucket
_images[imageIndex].setPixel(x, y, pixel);
}
}
_layers = await compute<List<image.Image>, List<Uint8List>>(
_encodeImages, _images);
//prepare random dislocations and set state
setState(() {
_randoms = List.generate(
widget.numberOfBuckets,
(i) => (math.Random().nextDouble() - 0.5) * 2,
);
});
//give a short delay to draw images
await Future.delayed(Duration(milliseconds: 100));
//start the snap!
_animationController.forward();
}
/// I am... IRON MAN ~Tony Stark
void reset() {
setState(() {
_layers = null;
_animationController.reset();
});
}
Widget _imageToWidget(Uint8List layer) {
//get layer's index in the list
int index = _layers.indexOf(layer);
//based on index, calculate when this layer should start and end
double animationStart = (index / _layers.length) * _lastLayerAnimationStart;
double animationEnd = animationStart + _singleLayerAnimationLength;
//create interval animation using only part of whole animation
CurvedAnimation animation = CurvedAnimation(
parent: _animationController,
curve: Interval(
animationStart,
animationEnd,
curve: Curves.easeOut,
),
);
Offset randomOffset = widget.randomDislocationOffset.scale(
_randoms[index],
_randoms[index],
);
Animation<Offset> offsetAnimation = Tween<Offset>(
begin: Offset.zero,
end: widget.offset + randomOffset,
).animate(animation);
return AnimatedBuilder(
animation: _animationController,
child: Image.memory(layer),
builder: (context, child) {
return Transform.translate(
offset: offsetAnimation.value,
child: Opacity(
opacity: math.cos(animation.value * math.pi / 2),
child: child,
),
);
},
);
}
/// Returns index of a randomly chosen bucket
int _pickABucket(List<int> weights, int sumOfWeights) {
int rnd = math.Random().nextInt(sumOfWeights);
int chosenImage = 0;
for (int i = 0; i < widget.numberOfBuckets; i++) {
if (rnd < weights[i]) {
chosenImage = i;
break;
}
rnd -= weights[i];
}
return chosenImage;
}
/// Gets an Image from a [child] and caches [size] for later us
Future<image.Image> _getImageFromWidget() async {
RenderRepaintBoundary boundary =
_globalKey.currentContext.findRenderObject();
//cache image for later
size = boundary.size;
var img = await boundary.toImage();
var byteData = await img.toByteData(format: ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
return image.decodeImage(pngBytes);
}
int _gauss(double center, double value) =>
(1000 * math.exp(-(math.pow((value - center), 2) / 0.14))).round();
}
/// This is slow! Run it in separate isolate
List<Uint8List> _encodeImages(List<image.Image> images) {
return images.map((img) => Uint8List.fromList(image.encodePng(img))).toList();
}
class XMargin extends StatelessWidget {
final double x;
const XMargin(this.x);
@override
Widget build(BuildContext context) {
return SizedBox(width: x);
}
}
class YMargin extends StatelessWidget {
final double y;
const YMargin(this.y);
@override
Widget build(BuildContext context) {
return SizedBox(height: y);
}
}
extension CustomContext on BuildContext {
double screenHeight([double percent = 1]) =>
MediaQuery.of(this).size.height * percent;
double screenWidth([double percent = 1]) =>
MediaQuery.of(this).size.width * percent;
}
final menu =
"""iVBORw0KGgoAAAANSUhEUgAAAC8AAAAnCAYAAACfdBHBAAAAAXNSR0IArs4c6QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAL6ADAAQAAAABAAAAJwAAAAD77K8MAAAA3ElEQVRYCe3ZzQ2CQBAF4DcrsQ0twZ9GsAQ6sANLsAY7wEI0WIGWgFcju86aoFzJJuCYNxdCwm4eX7g8Vp5Y5x7YCbCAiQl1AMopzoU8sLoKZG4idydkhmbp9F7RbY5zkG1AuBiKf9fP5iCoKkOZGZUCFKAABShAAQr8t4AMX0a+ZSKVNpaRm5aRWepGfddridhkOJV913Wfj2XE7IxRRt5lIlXdrDiDU4ACFKAABShAAQr8mMAITSpJoNQTkUL/zddxF+cR9nbOo5A3cHn7+jzWaSUGuB4n8J/e+wJhmTc9M2l3OAAAAABJRU5ErkJggg==""";
final box =
"""iVBORw0KGgoAAAANSUhEUgAAAGgAAAA/CAYAAAAMl43uAAAAAXNSR0IArs4c6QAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAaKADAAQAAAABAAAAPwAAAADWqGrwAAAEWklEQVR4Ae2dQW7TQBSGCeqesGWVI2TNpu4JgBPgnoACB2i2SCDlBs0SVvQGTfdI5AatxAGaJazC99xx5bbxeMae8TjpG+nhsd97/7z3/3FkeVIxeuY4NptNRuihY3hfYZej0WhZXYw6zzjPq9cGMF9RwxG1rn1rGbkm0PgNsWPX+J7i1jT9slyLGmfMT8vzgR1biXTg0cQxsVOP+D5CpeliIE7OpBTnHcKd33rc/iV/QuRVQ/RHcOcNMffc4I65sMSm2AXnR2CsmT+dQdM5Vo68TeckzwzA0szlvLSF8TUJuHVpcsfYtcH4LedbA/fxIs1OsRvT/Kxtj+SvDUa2DaPilzvBe5AvdZZr/PQG2MUE03QpzqJtD+DkmIxVHQa+eRGx2XRZpyrSWd1ae3EdssbYVVfShAwwVgYnryMH/8TEyAei9VcUufsvkhCEyXe5DCG3C2FZgcLXT5045XWzloTXClnG2o7k77dINHiByegkjpAIxqJAun1Et/EqsbmJvbAGOjjB2U+RaOzMkLTmOHHgojZE8g2WHBqxiBljsq6MxvjahY0DjP0SiYbmwgxDSJo2EdDkB2MmYIxFU2zpl9gig1rKa12OYJV3pcDOumAlzX3QSBaiGDCvhRWGM57EFhk8oISoQTDAq4qUh8LtDSdGAxXMlW8j5JbCvvXNrYuv1MO020NI3RpRrlOsfE+X4yTUIgAuDWjui0neick99821xYP56E4acTEj6dCWGNF3yXupZR0+tU3xif8F9gv7joUYrwD5hP3D5thfzGeMCf5gEr5w9M23rSU9vzEBxyLQmhMhIMW49zb6YQHUtuRaqg/Pw3KSnMvb7BwT1VKMleOiP4j74xjrEvaaILFLbIm1GRlJ8uHpgmFb99TmHIRP7iBMRhayIPBmBWqHx9oQGLaeTH2b57Yg9aVnQAVKr4G1AhXISk96pwqUXgNrBSqQlZ70ThUovQbWClQgKz3pnSpQeg2sFahAVnrSO1Wg9BpYK1CBrPSkd6pA6TWwVuD822xe3mUghX71b90Pslb+RJzOAsHHORZ630j2ou7+OuGJcO7Vpo9AOcih941c94O8mtqnYGeB2JqWO0hMR48M6ENCj2S3Wcr5DmoDvgM5pzz8DHprWe+gHfgUDbZEPt1RfpMw2IYrhdF7MfQOqpAyxKkKNERVKjWpQBUyhjhVgYaoSqUmFahCxhCnKtAQVanUpAJVyBji9ICH7YzCQm8juPaq2w0NTMmrHnkBGnoboWHZO/eamW433NHxeCIC5dj0sauXK6teVtFF4jDA16++6olDraKGYkCf4kIxGQlHBYpEbChYFSgUk5FwVKBIxIaCVYFCMRkJRwWKRGwoWBUoFJORcFSgSMSGglWBQjEZCWdXfhf3ntc+qd64R6LeDdZLIEj67AbrF8XPir81ZOQN/r11O//fDcIAAm1iMIFAW+tguZz1JjHW3BVMrzuIpr722Ri6Lfpcb4hr/QfhQRU4swELMgAAAABJRU5ErkJggg==""";
class FrameBuilder extends StatelessWidget {
final Widget app;
final TransitionBuilder builder;
const FrameBuilder({
this.app,
this.builder,
});
@override
Widget build(BuildContext context) {
return Frame(
app: builder(context, app),
);
}
}
class Frame extends StatelessWidget {
final Widget app;
const Frame({Key key, this.app}) : super(key: key);
static TransitionBuilder get builder => (context, app) => Frame(app: app);
@override
Widget build(BuildContext context) {
final theme = DefaultFrameTheme.of(context).data;
final shouldDisplayTemplate = MediaQuery.of(context).size.width > 600;
if (!shouldDisplayTemplate) {
return app;
} else {
final MediaQueryData mediaQuery = MediaQueryData(
size: Size(414, 896),
padding: EdgeInsets.only(
top: 44,
bottom: 34,
),
devicePixelRatio: 2,
);
return Container(
color: Colors.red.withOpacity(0.13),
padding: EdgeInsets.symmetric(vertical: 40),
child: FittedBox(
child: Material(
color: Colors.transparent,
child: Builder(builder: (context) {
final device = MediaQuery(
data: mediaQuery,
child: SizedBox.fromSize(
size: mediaQuery.size,
child: Stack(
fit: StackFit.expand,
children: <Widget>[
app,
Positioned(
top: 0,
left: 0,
right: 0,
height: 44,
child: _StatusBar(theme: theme),
),
Align(
alignment: Alignment.bottomCenter,
child: Container(
margin: EdgeInsets.only(bottom: 8),
height: 4,
width: 140,
decoration: BoxDecoration(
color: theme.statusBarColor,
borderRadius: BorderRadius.circular(4)),
),
)
],
)),
);
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
XMargin(context.screenWidth(0.12)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 60,
width: 60,
decoration: BoxDecoration(
color: Colors.black12,
borderRadius: BorderRadius.circular(5)),
child: Center(
child: Text(
'✊🏾',
style: TextStyle(fontSize: 30),
),
)),
YMargin(30),
Snappable(
key: key[7],
child: Text(
"I am\nInevitable",
style: TextStyle(
fontSize: 40,
color: Colors.black,
fontWeight: FontWeight.w800),
),
),
YMargin(20),
Snappable(
key: key[8],
child: Container(
width: 350,
child: Text(
"I used the stones to destroy the stones. And it nearly killed me. But the work is done, it always will be. I am inevitable.",
style: TextStyle(
fontSize: 16,
height: 1.4,
color: Colors.grey,
fontWeight: FontWeight.w300),
),
),
),
YMargin(30),
Row(
children: [
Material(
color: Colors.black,
child: InkWell(
onTap: () async {
key.forEach((_) async {
await _.currentState.snap();
});
},
child: Padding(
padding: EdgeInsets.all(20),
child: Text(
'Vanish',
style: TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w300),
),
),
),
),
XMargin(30),
Material(
color: Colors.red,
child: InkWell(
onTap: () async {
key.forEach((_) async {
_.currentState.reset();
});
},
child: Padding(
padding: EdgeInsets.all(20),
child: Text(
'I am IronMan',
style: TextStyle(
fontSize: 14,
color: Colors.white,
fontWeight: FontWeight.w300),
),
),
),
),
],
),
],
),
XMargin(context.screenWidth(0.1)),
Container(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: device,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.2),
blurRadius: 43),
],
),
),
],
),
);
}),
),
),
);
}
}
}
class _StatusBar extends StatelessWidget {
final FrameThemeData theme;
const _StatusBar({Key key, this.theme}) : super(key: key);
@override
Widget build(BuildContext context) {
final date = DateTime.now();
return Theme(
data: ThemeData(brightness: theme.statusBarBrightness),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 30, top: 4),
child: Text(
'${date.hour}:${date.minute}',
style: TextStyle(
fontWeight: FontWeight.bold, color: theme.statusBarColor),
)),
Padding(
padding: EdgeInsets.only(right: 18),
child: Row(
children: <Widget>[
ImageIcon(
NetworkImage(
'https://github.com/google/material-design-icons/raw/master/png/device/signal_cellular_4_bar/materialiconsround/36dp/2x/round_signal_cellular_4_bar_black_36dp.png'),
size: 14,
color: theme.statusBarColor),
SizedBox(width: 4),
ImageIcon(
NetworkImage(
'https://github.com/google/material-design-icons/raw/master/png/device/signal_wifi_4_bar/materialiconsround/36dp/2x/round_signal_wifi_4_bar_black_36dp.png'),
size: 16,
color: theme.statusBarColor),
SizedBox(width: 4),
RotatedBox(
quarterTurns: 1,
child: ImageIcon(
NetworkImage(
'https://github.com/google/material-design-icons/raw/master/png/device/battery_charging_60/materialiconstwotone/36dp/2x/twotone_battery_charging_60_black_36dp.png'),
size: 19,
color: theme.statusBarColor),
)
],
),
)
],
),
);
}
}
class FrameThemeData {
final Color frameColor;
final Brightness statusBarBrightness;
factory FrameThemeData({Color frameColor, Brightness statusBarBrightness}) {
frameColor ??= Colors.white;
statusBarBrightness ??= Brightness.light;
return FrameThemeData.raw(
frameColor: frameColor,
statusBarBrightness: statusBarBrightness,
);
}
const FrameThemeData.raw({
this.frameColor,
this.statusBarBrightness,
}) : assert(frameColor != null),
assert(statusBarBrightness != null);
Color get statusBarColor =>
statusBarBrightness == Brightness.dark ? Colors.white : Colors.black;
}
class DefaultFrameTheme extends InheritedTheme {
final FrameThemeData data;
DefaultFrameTheme({
Key key,
this.data,
@required Widget child,
}) : super(key: key, child: child);
static DefaultFrameTheme of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<DefaultFrameTheme>() ??
DefaultFrameTheme.fallback();
}
DefaultFrameTheme.fallback() : data = FrameThemeData();
@override
bool updateShouldNotify(DefaultFrameTheme oldWidget) {
return data != oldWidget.data;
}
@override
Widget wrap(BuildContext context, Widget child) {
final DefaultFrameTheme defaultSpacing =
context.findAncestorWidgetOfExactType<DefaultFrameTheme>();
return identical(this, defaultSpacing)
? child
: DefaultFrameTheme(
data: data,
child: child,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment