Created with <3 with dartpad.dev.
Last active
September 14, 2023 15:07
-
-
Save phanatagama/65c4a6e29ed171c4e05d0f8abaa64d39 to your computer and use it in GitHub Desktop.
Stack Widget
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 'dart:ui' as ui; | |
//Add this CustomPaint widget to the Widget Tree | |
const primaryColor = Color(0xFF40128B); | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) => MaterialApp(home: MyPage()); | |
} | |
class MyPage extends StatefulWidget { | |
@override | |
MyPageState createState() => MyPageState(); | |
} | |
class MyPageState extends State<MyPage> with WidgetsBindingObserver { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: SafeArea( | |
child: _buildColumn(), | |
), | |
); | |
} | |
Widget _buildColumn() => Column( | |
crossAxisAlignment: CrossAxisAlignment.stretch, | |
children: [ | |
// First blue container | |
_buildTopContainer(), | |
// Button with offset | |
// _buildMidContainerWithButton(), | |
_buildMidC(), | |
// Bottom white container | |
_buildBottomContainer(), | |
], | |
); | |
Widget _buildTopContainer() => Stack( | |
// flex: 2, | |
// fit: StackFit.expand, | |
children: [ | |
ClipPath( | |
clipper: ArcClipper(), | |
child: Container( | |
color: primaryColor, | |
padding: const EdgeInsets.all(16), | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Row(children: [ | |
const Text('OVO', | |
style: TextStyle( | |
color: Colors.white, | |
fontSize: 20, | |
fontWeight: FontWeight.w600)), | |
const Spacer(), | |
IconButton( | |
icon: const Icon( | |
Icons.notification_add, | |
color: Colors.white, | |
), | |
tooltip: 'Increase volume by 10', | |
onPressed: () {}, | |
), | |
]), | |
Text( | |
'OVO Cash', | |
style: TextStyle(color: Colors.white, fontSize: 16), | |
), | |
RichText( | |
text: TextSpan( | |
style: TextStyle(color: Colors.white, fontSize: 16), | |
children: [ | |
// TextSpan(text: 'Loh'), | |
WidgetSpan( | |
alignment: PlaceholderAlignment.top, | |
child: Text('Rp ', | |
style: TextStyle( | |
fontSize: 12, color: Colors.white)), | |
), | |
TextSpan( | |
text: '4.980.000', | |
style: TextStyle( | |
fontWeight: FontWeight.bold, fontSize: 24)), | |
], | |
), | |
), | |
SizedBox(height: 100), | |
Text( | |
'Top container', | |
style: TextStyle( | |
fontSize: 17.0, | |
fontWeight: FontWeight.w600, | |
color: Colors.white, | |
), | |
), | |
], | |
), | |
), | |
), | |
Positioned( | |
bottom: 0, | |
left: 0, | |
right: 0, | |
child: ClipPath( | |
clipper: WaveClipper(), | |
child: Container( | |
color: Colors.white.withOpacity(0.1), | |
width: double.infinity, | |
height: 100, | |
), | |
), | |
), | |
]); | |
Widget _buildMidC() { | |
return Stack( | |
children: [ | |
Container( | |
height: 100, | |
color: Colors.red, | |
child: const Center( | |
child: Text('First stack element'), | |
), | |
), | |
Transform.translate( | |
offset: const Offset(0.0, -100 / 2), | |
child: Center( | |
child: Container( | |
padding: const EdgeInsets.all(24.0), | |
margin: EdgeInsets.all(16.0), | |
decoration: BoxDecoration( | |
color: Colors.blue[600], | |
borderRadius: BorderRadius.circular(32.0 / 2.0), | |
boxShadow: [ | |
BoxShadow( | |
blurRadius: 16.0, | |
offset: const Offset(0.0, 6.0), | |
color: Colors.black.withOpacity(0.16), | |
), | |
], | |
), | |
child: const Wrap( | |
// mainAxisSize: MainAxisSize.min, | |
children: [ | |
Column( | |
children: [ | |
Icon( | |
Icons.photo_camera_outlined, | |
size: 20.0, | |
color: Colors.white, | |
), | |
Text('Jadwal Shalat'), | |
], | |
), | |
Column( | |
children: [ | |
Icon( | |
Icons.photo_camera_outlined, | |
size: 20.0, | |
color: Colors.white, | |
), | |
Text('Jadwal Shalat'), | |
], | |
), | |
Column( | |
children: [ | |
Icon( | |
Icons.photo_camera_outlined, | |
size: 20.0, | |
color: Colors.white, | |
), | |
Text('Jadwal Shalat'), | |
], | |
), | |
Column( | |
children: [ | |
Icon( | |
Icons.photo_camera_outlined, | |
size: 20.0, | |
color: Colors.white, | |
), | |
Text('Jadwal Shalat'), | |
], | |
), | |
Column( | |
children: [ | |
Icon( | |
Icons.photo_camera_outlined, | |
size: 20.0, | |
color: Colors.white, | |
), | |
Text('Jadwal Shalat'), | |
], | |
), | |
], | |
), | |
), | |
), | |
), | |
], | |
); | |
} | |
Widget buildMidContainerWithButton() { | |
const buttonHeight = 50.0; | |
return Stack( | |
children: [ | |
// Use same background color like the second container | |
//Container(height: 5, color: Colors.red), | |
// Translate the button | |
Transform.translate( | |
offset: const Offset(0.0, -buttonHeight / 2.0), | |
child: Center( | |
child: GestureDetector( | |
onTap: () {/* do stuff */}, | |
child: Container( | |
height: buttonHeight, | |
decoration: BoxDecoration( | |
color: Colors.orange[400], | |
borderRadius: BorderRadius.circular(buttonHeight / 2.0), | |
boxShadow: [ | |
BoxShadow( | |
blurRadius: 16.0, | |
offset: const Offset(0.0, 6.0), | |
color: Colors.black.withOpacity(0.16), | |
), | |
], | |
), | |
padding: const EdgeInsets.fromLTRB(24.0, 3.0, 24.0, 0.0), | |
child: const Row( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
Icon( | |
Icons.photo_camera_outlined, | |
size: 20.0, | |
color: Colors.white, | |
), | |
Padding( | |
padding: EdgeInsets.only(left: 8.0), | |
child: Text( | |
'Use camera', | |
style: TextStyle( | |
fontSize: 17.0, | |
fontWeight: FontWeight.w600, | |
color: Colors.white, | |
), | |
), | |
), | |
], | |
), | |
), | |
), | |
), | |
), | |
], | |
); | |
} | |
Widget _buildBottomContainer() => Flexible( | |
flex: 8, | |
child: Container( | |
color: Colors.white, | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
CustomPaint( | |
size: const Size(22, 27), | |
painter: RPSCustomPainter(), | |
), | |
const Text( | |
'Bottom container', | |
style: TextStyle( | |
fontSize: 17.0, | |
fontWeight: FontWeight.w600, | |
color: Colors.black54, | |
), | |
), | |
]), | |
), | |
); | |
} | |
class WaveClipper extends CustomClipper<Path> { | |
@override | |
Path getClip(Size size) { | |
var path = Path(); | |
path.lineTo(0, size.height - 30); | |
// path.lineTo(size.width, size.height); | |
var firstControlPoint = Offset(size.width / 4, size.height); | |
var firstPoint = Offset(size.width / 2, size.height); | |
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy, | |
firstPoint.dx, firstPoint.dy); | |
var secondControlPoint = Offset(size.width - (size.width / 4), size.height); | |
var secondPoint = Offset(size.width, size.height - 30); | |
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy, | |
secondPoint.dx, secondPoint.dy); | |
// path.quadraticBezierTo(size.width/3, size.height, size.width/2, size.height); | |
// path.quadraticBezierTo(size.width - (size.width/3), size.height, size.width, size.height-(size.height/3)); | |
path.lineTo(size.width, 10); | |
path.quadraticBezierTo( | |
size.width - 50, size.height / 5, size.width - 100, size.height / 10); | |
path.quadraticBezierTo((size.width / 2) + 30, 0, size.width / 2, 30); | |
path.quadraticBezierTo((size.width / 4) + 30, 60, size.width / 6, 30); | |
path.quadraticBezierTo(size.width / 8, 10, 0, 30); | |
// path.quadraticBezierTo(size.width/4, size.height/2, size.width/5, 0); | |
path.close(); | |
return path; | |
} | |
@override | |
bool shouldReclip(CustomClipper<Path> oldClipper) => false; | |
} | |
class ArcClipper extends CustomClipper<Path> { | |
@override | |
Path getClip(Size size) { | |
var path = Path(); | |
path.lineTo(0.0, size.height - 30); | |
var firstControlPoint = Offset(size.width / 4, size.height); | |
var firstPoint = Offset(size.width / 2, size.height); | |
path.quadraticBezierTo(firstControlPoint.dx, firstControlPoint.dy, | |
firstPoint.dx, firstPoint.dy); | |
var secondControlPoint = Offset(size.width - (size.width / 4), size.height); | |
var secondPoint = Offset(size.width, size.height - 30); | |
path.quadraticBezierTo(secondControlPoint.dx, secondControlPoint.dy, | |
secondPoint.dx, secondPoint.dy); | |
path.lineTo(size.width, 0.0); | |
path.close(); | |
return path; | |
} | |
@override | |
bool shouldReclip(CustomClipper<Path> oldClipper) => false; | |
} | |
//Copy this CustomPainter code to the Bottom of the File | |
class RPSCustomPainter extends CustomPainter { | |
@override | |
void paint(Canvas canvas, Size size) { | |
Path path_0 = Path(); | |
path_0.moveTo(10.9997, 13.5001); | |
path_0.cubicTo(9.53301, 13.5001, 8.33301, 12.3001, 8.33301, 10.8334); | |
path_0.cubicTo(8.33301, 9.36675, 9.53301, 8.16675, 10.9997, 8.16675); | |
path_0.cubicTo(12.4663, 8.16675, 13.6663, 9.36675, 13.6663, 10.8334); | |
path_0.cubicTo(13.6663, 12.3001, 12.4663, 13.5001, 10.9997, 13.5001); | |
path_0.close(); | |
path_0.moveTo(18.9997, 11.1001); | |
path_0.cubicTo(18.9997, 6.26008, 15.4663, 2.83341, 10.9997, 2.83341); | |
path_0.cubicTo(6.53301, 2.83341, 2.99967, 6.26008, 2.99967, 11.1001); | |
path_0.cubicTo(2.99967, 14.2201, 5.59967, 18.3534, 10.9997, 23.2867); | |
path_0.cubicTo(16.3997, 18.3534, 18.9997, 14.2201, 18.9997, 11.1001); | |
path_0.close(); | |
path_0.moveTo(10.9997, 0.166748); | |
path_0.cubicTo(16.5997, 0.166748, 21.6663, 4.46008, 21.6663, 11.1001); | |
path_0.cubicTo(21.6663, 15.5267, 18.1063, 20.7667, 10.9997, 26.8334); | |
path_0.cubicTo(3.89301, 20.7667, 0.333008, 15.5267, 0.333008, 11.1001); | |
path_0.cubicTo(0.333008, 4.46008, 5.39967, 0.166748, 10.9997, 0.166748); | |
path_0.close(); | |
Paint paint_0_fill = Paint()..style = PaintingStyle.fill; | |
paint_0_fill.color = Color(0xff28C76F).withOpacity(1.0); | |
canvas.drawPath(path_0, paint_0_fill); | |
} | |
@override | |
bool shouldRepaint(covariant CustomPainter oldDelegate) { | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment