Skip to content

Instantly share code, notes, and snippets.

@imaNNeo
Created November 13, 2020 11:38
Show Gist options
  • Save imaNNeo/0d98a1c109e8ac2232f6d51cd6d7913f to your computer and use it in GitHub Desktop.
Save imaNNeo/0d98a1c109e8ac2232f6d51cd6d7913f to your computer and use it in GitHub Desktop.
import 'dart:ui';
import 'dart:math' as math;
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter 4 Fun',
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
double progress = 10;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text('Flutter4Fun.com'),
),
backgroundColor: Color(0xffFF6958),
body: Center(
child: HeroRowWidget(
hero: heroes[0],
),
),
);
}
}
final String heart = 'https://flutter4fun.com/wp-content/uploads/2020/11/heart-alt.png';
final String knife = 'https://flutter4fun.com/wp-content/uploads/2020/11/knife.png';
final String speed = 'https://flutter4fun.com/wp-content/uploads/2020/11/speed.png';
class HeroModel {
final String name;
final String image;
final double imageRotationAngle;
final double speed, health, attack;
HeroModel({
@required this.name,
@required this.image,
this.imageRotationAngle = 0.0,
@required this.speed,
@required this.health,
@required this.attack,
});
}
final List<HeroModel> heroes = [
HeroModel(
name: 'Bombardier',
image: 'https://flutter4fun.com/wp-content/uploads/2020/11/Player-1.png',
speed: 16.0,
health: 40.0,
attack: 65.0,
),
HeroModel(
name: 'Bombardier',
image: 'https://flutter4fun.com/wp-content/uploads/2020/11/Player-2.png',
speed: 25.0,
health: 50.0,
attack: 75.0,
),
HeroModel(
name: 'Bombardier',
image: 'https://flutter4fun.com/wp-content/uploads/2020/11/Player-3.png',
speed: 25.0,
health: 50.0,
attack: 75.0,
),
];
// Degree / Radians converter
const double degrees2Radians = math.pi / 180.0;
const double radians2Degrees = 180.0 / math.pi;
double degrees(double radians) => radians * radians2Degrees;
double radians(double degrees) => degrees * degrees2Radians;
class HeroRowWidget extends StatelessWidget {
final HeroModel hero;
final double rowHeight;
const HeroRowWidget({
@required this.hero,
this.rowHeight = 282,
});
@override
Widget build(BuildContext context) {
return Container(
height: rowHeight,
child: Stack(
alignment: Alignment.center,
children: [
Transform.translate(
offset: Offset(-10, 0),
child: Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.01)
..rotateY(radians(1.5)),
child: Container(
height: 216,
margin: EdgeInsets.symmetric(horizontal: 40),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
borderRadius: BorderRadius.all(Radius.circular(22)),
),
),
),
),
Transform.translate(
offset: Offset(-44, 0),
child: Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.01)
..rotateY(radians(8)),
child: Container(
height: 188,
margin: EdgeInsets.symmetric(horizontal: 40),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.4),
borderRadius: BorderRadius.all(Radius.circular(22)),
),
),
),
),
Row(
children: [
Transform.translate(
offset: Offset(-30, 0),
child: Container(
child: Image.network(
'https://flutter4fun.com/wp-content/uploads/2020/11/Player-1.png',
width: rowHeight,
height: rowHeight,
),
),
),
],
),
Align(
alignment: Alignment.centerRight,
child: Container(
margin: EdgeInsets.only(right: 58),
padding: EdgeInsets.symmetric(vertical: 34),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
AttributeWidget(
progress: hero.speed,
child: Image.network(speed),
),
AttributeWidget(
progress: hero.health,
child: Image.network(heart),
),
AttributeWidget(
progress: hero.attack,
child: Image.network(knife),
),
SizedBox(
height: 32,
child: OutlineButton(
child: new Text(
'See Details',
style: TextStyle(
color: Colors.white,
fontSize: 12,
),
),
onPressed: () {},
color: Colors.white,
borderSide: BorderSide(
color: Colors.white,
width: 1,
),
highlightedBorderColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
),
)
],
),
),
)
],
),
);
}
}
class AttributeWidget extends StatelessWidget {
final double size;
final double progress;
final Widget child;
const AttributeWidget({
Key key,
@required this.progress,
this.size = 42,
this.child,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return CustomPaint(
painter: AttributePainter(
progressPercent: progress,
),
size: Size(size, size),
child: Container(
padding: EdgeInsets.all(size / 3.8),
width: size,
height: size,
child: child,
),
);
}
}
class AttributePainter extends CustomPainter {
final double progressPercent;
final double strokeWidth, filledStrokeWidth;
final bgPaint, strokeBgPaint, strokeFilledPaint;
AttributePainter({
this.progressPercent,
this.strokeWidth = 2.0,
this.filledStrokeWidth = 4.0,
}) : bgPaint = Paint()..color = Colors.white.withOpacity(0.25),
strokeBgPaint = Paint()..color = Color(0xffD264C9),
strokeFilledPaint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = filledStrokeWidth
..strokeCap = StrokeCap.round;
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
canvas.drawCircle(center, radius, bgPaint);
canvas.drawCircle(center, radius - strokeWidth, strokeBgPaint);
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius - (strokeWidth / 2)),
-math.pi / 2, // - 45 degrees to start from top
(progressPercent / 100) * math.pi * 2,
false,
strokeFilledPaint,
);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment