Created
January 6, 2020 09:53
-
-
Save zonble/303d28bce04b8e995a0174ef24b0174e to your computer and use it in GitHub Desktop.
This file contains hidden or 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:math' as math; | |
import 'dart:ui'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: Text(widget.title)), | |
body: TurtleView( | |
child: Container(), | |
commands: [ | |
PenDown(), | |
SetColor(() => Color(0xffff9933)), | |
Repeat(() => 20, [ | |
Repeat(() => 180, [ | |
Forward(() => 25.0), | |
Right(() => 20), | |
]), | |
Right(() => 18), | |
]), | |
PenUp(), | |
], | |
), | |
); | |
} | |
} | |
class _TurtlePainter extends CustomPainter { | |
final List<TurtleCommand> commands; | |
_TurtlePainter(this.commands); | |
@override | |
void paint(Canvas canvas, Size size) { | |
var turtle = TurtleState(); | |
var paint = Paint() | |
..color = Colors.black | |
..strokeWidth = 2; | |
var center = Offset(size.width / 2, size.height / 2); | |
for (final command in commands) { | |
command.exec(turtle, canvas, paint, center); | |
} | |
} | |
@override | |
bool shouldRepaint(CustomPainter oldDelegate) => true; | |
} | |
class TurtleView extends StatelessWidget { | |
final List<TurtleCommand> commands; | |
final Widget child; | |
TurtleView({Key key, this.child, this.commands}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return CustomPaint( | |
painter: _TurtlePainter(commands), | |
child: child, | |
); | |
} | |
} | |
/// Represents the state of a turtle. | |
class TurtleState { | |
/// If the pen is down. | |
/// | |
/// Or, if we should paint a line instead of merely moves the [position] of | |
/// the turtle while calling the [Forward] command. | |
bool isPenDown = false; | |
/// The position of the turtle. | |
/// | |
/// The turtle is in the center of a canvas by default. | |
Offset position = Offset(0.0, 0.0); | |
/// The angle of the turtle. | |
/// | |
/// It effect how the turtle moves while calling a [Forward] command. | |
double angle = 90.0; | |
} | |
/// An abstract interface for all commands. | |
abstract class TurtleCommand { | |
/// Runs the command. | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center); | |
} | |
/// Puts the pen down. | |
@immutable | |
class PenDown implements TurtleCommand { | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
turtle.isPenDown = true; | |
} | |
/// Raises the pen up. | |
@immutable | |
class PenUp implements TurtleCommand { | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
turtle.isPenDown = false; | |
} | |
/// Turns left. | |
@immutable | |
class Left implements TurtleCommand { | |
/// the angle. | |
final double Function() angle; | |
/// Creates a new instance. | |
Left(this.angle); | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
turtle.angle += angle(); | |
} | |
/// Turns right. | |
@immutable | |
class Right implements TurtleCommand { | |
/// the right. | |
final double Function() angle; | |
/// Creates a new instance. | |
Right(this.angle); | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
turtle.angle -= angle(); | |
} | |
_angleToRadians(double angle) => angle / 180 * math.pi; | |
/// Moves forward. | |
@immutable | |
class Forward implements TurtleCommand { | |
/// The distance. | |
final double Function() distance; | |
/// Creates a new instance. | |
Forward(this.distance); | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) { | |
final radians = _angleToRadians(turtle.angle); | |
final distance = this.distance(); | |
final x = math.cos(radians) * distance; | |
final y = math.sin(radians) * distance; | |
final currentPosition = turtle.position; | |
turtle.position = currentPosition + Offset(x, y); | |
if (turtle.isPenDown) { | |
final drawingBegin = center + currentPosition; | |
final drawingEnd = center + turtle.position; | |
canvas.drawLine(drawingBegin, drawingEnd, paint); | |
} | |
} | |
} | |
/// Sets a new color. | |
@immutable | |
class SetColor implements TurtleCommand { | |
/// The new color. | |
final Color Function() color; | |
/// Creates a new color. | |
SetColor(this.color); | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
paint.color = color(); | |
} | |
/// Moves the turtle to center. | |
@immutable | |
class ResetPosition implements TurtleCommand { | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
turtle.position = Offset(0.0, 0.0); | |
} | |
/// Makes the turtle to face to top. | |
@immutable | |
class ResetHeading implements TurtleCommand { | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) => | |
turtle.angle = 90; | |
} | |
/// Repeats commands | |
@immutable | |
class Repeat implements TurtleCommand { | |
/// How many times to repeat. | |
final int Function() times; | |
/// The commands to run. | |
final List<TurtleCommand> commands; | |
/// Creates a new instance. | |
Repeat(this.times, this.commands); | |
@override | |
void exec(TurtleState turtle, Canvas canvas, Paint paint, Offset center) { | |
for (var i = 0; i < times(); i++) { | |
commands.forEach((command) { | |
command.exec(turtle, canvas, paint, center); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment