blob: afa12e377c48eac06709ae97e0902468286a53e2 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: public_member_api_docs
import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:sensors/sensors.dart';
class Snake extends StatefulWidget {
Snake({this.rows = 20, this.columns = 20, this.cellSize = 10.0}) {
assert(10 <= rows);
assert(10 <= columns);
assert(5.0 <= cellSize);
}
final int rows;
final int columns;
final double cellSize;
@override
State<StatefulWidget> createState() => SnakeState(rows, columns, cellSize);
}
class SnakeBoardPainter extends CustomPainter {
SnakeBoardPainter(this.state, this.cellSize);
GameState state;
double cellSize;
@override
void paint(Canvas canvas, Size size) {
final Paint blackLine = Paint()..color = Colors.black;
final Paint blackFilled = Paint()
..color = Colors.black
..style = PaintingStyle.fill;
canvas.drawRect(
Rect.fromPoints(Offset.zero, size.bottomLeft(Offset.zero)),
blackLine,
);
for (math.Point<int> p in state.body) {
final Offset a = Offset(cellSize * p.x, cellSize * p.y);
final Offset b = Offset(cellSize * (p.x + 1), cellSize * (p.y + 1));
canvas.drawRect(Rect.fromPoints(a, b), blackFilled);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class SnakeState extends State<Snake> {
SnakeState(int rows, int columns, this.cellSize)
: state = GameState(rows, columns);
double cellSize;
GameState state;
AccelerometerEvent? acceleration;
late StreamSubscription<AccelerometerEvent> _streamSubscription;
late Timer _timer;
@override
Widget build(BuildContext context) {
return CustomPaint(painter: SnakeBoardPainter(state, cellSize));
}
@override
void dispose() {
super.dispose();
_streamSubscription.cancel();
_timer.cancel();
}
@override
void initState() {
super.initState();
_streamSubscription =
accelerometerEvents.listen((AccelerometerEvent event) {
setState(() {
acceleration = event;
});
});
_timer = Timer.periodic(const Duration(milliseconds: 200), (_) {
setState(() {
_step();
});
});
}
void _step() {
final AccelerometerEvent? currentAcceleration = acceleration;
final math.Point<int>? newDirection = currentAcceleration == null
? null
: currentAcceleration.x.abs() < 1.0 && currentAcceleration.y.abs() < 1.0
? null
: (currentAcceleration.x.abs() < currentAcceleration.y.abs())
? math.Point<int>(0, currentAcceleration.y.sign.toInt())
: math.Point<int>(-currentAcceleration.x.sign.toInt(), 0);
state.step(newDirection);
}
}
class GameState {
GameState(this.rows, this.columns)
: snakeLength = math.min(rows, columns) - 5;
int rows;
int columns;
int snakeLength;
List<math.Point<int>> body = <math.Point<int>>[const math.Point<int>(0, 0)];
math.Point<int> direction = const math.Point<int>(1, 0);
void step(math.Point<int>? newDirection) {
math.Point<int> next = body.last + direction;
next = math.Point<int>(next.x % columns, next.y % rows);
body.add(next);
if (body.length > snakeLength) body.removeAt(0);
direction = newDirection ?? direction;
}
}