Skip to content

Instantly share code, notes, and snippets.

@Metal-666
Created February 3, 2022 11:34
Show Gist options
  • Select an option

  • Save Metal-666/68609884d53ab6d4d714d75385d23368 to your computer and use it in GitHub Desktop.

Select an option

Save Metal-666/68609884d53ab6d4d714d75385d23368 to your computer and use it in GitHub Desktop.
Challenge 4 (BLoC)
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:ui' as ui;
import 'dart:math';
void main() => runApp(App());
//------------------------------Widgets-----------------------------
class App extends StatelessWidget {
final Color _primaryColor = Colors.deepPurple[700]!;
final ThemeData _theme = ThemeData.dark();
@override
Widget build(BuildContext context) => BlocProvider<RootBloc>(
create: (BuildContext context) => RootBloc(),
child: MaterialApp(
theme: _theme.copyWith(
colorScheme: _theme.colorScheme
.copyWith(primary: _primaryColor, secondary: Colors.white),
appBarTheme: AppBarTheme(color: _primaryColor)),
home: Scaffold(
appBar: _appBar(),
body: SizedBox.expand(
child: _root(),
),
),
));
PreferredSizeWidget _appBar() => AppBar(
title: const Text('Clock'),
);
Widget _root() => Stack(children: <Widget>[
_fabs(<Widget>[_randomFAB(), const SizedBox(width: 10), _numbersFAB()]),
_clock()
]);
Widget _fabs(List<Widget> fabs) =>
Positioned(bottom: 10, right: 10, child: Row(children: fabs));
Widget _randomFAB() => Builder(
builder: (context) => FloatingActionButton(
onPressed: () => context.read<RootBloc>().add(RandomizeTime()),
child: const Icon(Icons.shuffle),
),
);
Widget _numbersFAB() => Builder(
builder: (context) => FloatingActionButton(
onPressed: () => context.read<RootBloc>().add(ToggleNumberStyle()),
child: const Icon(Icons.refresh),
),
);
Widget _clock() =>
Center(child: SizedBox(height: 300, width: 300, child: Clock()));
}
class Clock extends StatelessWidget {
Widget _clockHand(double turns, double margin, double extent,
double thiccness, Color handColor, Color dotColor) =>
AnimatedRotation(
turns: turns,
curve: Curves.easeInOutCubic,
duration: const Duration(milliseconds: 500),
child: CustomPaint(
painter: ClockHandPainter(
margin, extent, thiccness, handColor, dotColor)));
@override
Widget build(BuildContext context) => BlocBuilder<RootBloc, RootState>(
builder: (BuildContext context, RootState state) =>
Stack(fit: StackFit.expand, children: <Widget>[
CustomPaint(painter: ClockPainter(state.numberStyle)),
_clockHand(state.dateTime.second / 60, 30, 40, 3,
Colors.deepPurple[500]!, Colors.white),
_clockHand(state.dateTime.minute / 60, 40, 30, 3.5, Colors.white,
Colors.deepPurple[800]!),
_clockHand(state.dateTime.hour / 12, 60, 20, 4, Colors.white,
Colors.deepPurple[800]!)
]));
}
class ClockPainter extends CustomPainter {
final NumberStyle style;
ClockPainter(this.style);
@override
void paint(Canvas canvas, Size size) {
//Create our paints and colors
final Paint background = Paint()..color = Colors.deepPurple[500]!,
face = Paint()
..shader = ui.Gradient.linear(
Offset.zero,
Offset(size.width, size.height),
[
Colors.deepPurple[600]!,
Colors.deepPurple[900]!,
],
),
bigMark = (Paint()..color = Colors.white)..strokeWidth = 2,
smallMark = (Paint()..color = Colors.grey[400]!)..strokeWidth = 1;
Color numbers = Colors.white;
//Calculate some useful values
final double radius = size.height / 2,
centerX = size.width / 2,
centerY = size.height / 2;
//We might need this in the future?
//canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));
//Draw clock background (outer circle)
canvas.drawCircle(Offset(centerX, centerY), radius, background);
//Draw clock face (inner circle)
canvas.drawCircle(Offset(centerX, centerY), radius - 5, face);
//Draw the dot in the middle (idk what it's called)
canvas.drawCircle(Offset(centerX, centerY), 5, background);
//Draw hour marks and minute marks
_drawRadialMarks(
12, 13, 10, centerX, centerY, radius - 20, canvas, bigMark);
_drawRadialMarks(
12 * 5, 5, 7, centerX, centerY, radius - 17, canvas, smallMark);
//Draw numbers
_drawRadialText(
12, centerX, centerY, radius - 35, _getClockNumbers(), canvas, numbers);
}
void _drawRadialMarks(
int howMany,
int skipEach,
double length,
double centerX,
double centerY,
double radius,
Canvas canvas,
Paint paint) {
for (int i = 1; i <= howMany; i++) {
if (i % skipEach != 0) {
final double angle = i * (360 / howMany) * pi / 180;
final double x1 = centerX + radius * cos(angle),
y1 = centerY - radius * sin(angle);
final double x2 = centerX + (radius + length) * cos(angle),
y2 = centerY - (radius + length) * sin(angle);
canvas.drawLine(Offset(x1, y1), Offset(x2, y2), paint);
}
}
}
void _drawRadialText(int howMany, double centerX, double centerY,
double radius, List<String> strings, Canvas canvas, Color textColor) {
final TextStyle style = TextStyle(
color: textColor,
fontSize: 20,
);
for (int i = 0; i < howMany; i++) {
final TextSpan text = TextSpan(
text: strings.length > i ? strings[i] : 'uh',
style: style,
);
final TextPainter painter = TextPainter(
text: text,
textDirection: TextDirection.ltr,
)..layout(maxWidth: 100, minWidth: 0);
final double angle = (i * (360 / howMany) + 120) * pi / 180;
final double x = centerX - radius * cos(angle),
y = centerY - radius * sin(angle);
painter.paint(
canvas, Offset(x - painter.width / 2, y - painter.height / 2));
}
}
//Returns a set of numbers depending on current number style (roman|arabic)
List<String> _getClockNumbers() {
switch (style) {
case NumberStyle.arabic:
{
return <String>[
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12'
];
}
case NumberStyle.roman:
{
return <String>[
'I',
'II',
'III',
'IV',
'V',
'VI',
'VII',
'VIII',
'IX',
'X',
'XI',
'XII'
];
}
default:
{
return <String>['Huh?'];
}
}
}
@override
bool shouldRepaint(covariant ClockPainter oldDelegate) =>
oldDelegate.style != style;
}
class ClockHandPainter extends CustomPainter {
final double margin;
final double extent;
final double thiccness;
final Color handColor;
final Color dotColor;
const ClockHandPainter(
this.margin, this.extent, this.thiccness, this.handColor, this.dotColor);
@override
void paint(Canvas canvas, Size size) {
final Paint dotPaint = Paint()..color = dotColor,
handPaint = (Paint()..color = handColor)..strokeWidth = thiccness;
final double centerX = size.width / 2, centerY = size.height / 2;
canvas.drawCircle(Offset(centerX, centerY), 6, handPaint);
canvas.drawLine(
Offset(centerX, centerY + extent), Offset(centerX, margin), handPaint);
canvas.drawCircle(Offset(centerX, centerY), 3, dotPaint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
//------------------------------Widgets-----------------------------
//------------------------------Events------------------------------
abstract class RootEvents {}
class ToggleNumberStyle extends RootEvents {}
class RandomizeTime extends RootEvents {}
//------------------------------Events------------------------------
//------------------------------States------------------------------
class RootState {
final NumberStyle numberStyle;
final DateTime dateTime;
RootState(this.numberStyle, this.dateTime);
RootState changeNumberStyle(NumberStyle numberStyle) =>
RootState(numberStyle, dateTime);
RootState changeDateTime(DateTime dateTime) =>
RootState(numberStyle, dateTime);
}
enum NumberStyle { roman, arabic }
//------------------------------States------------------------------
//-------------------------------Bloc-------------------------------
class RootBloc extends Bloc<RootEvents, RootState> {
final Random random = Random();
RootBloc() : super(RootState(NumberStyle.roman, DateTime.now())) {
//Toggle numberStyle
on<ToggleNumberStyle>((event, emit) => emit(state.changeNumberStyle(
state.numberStyle == NumberStyle.roman
? NumberStyle.arabic
: NumberStyle.roman)));
//Generate a new DateTime (from 0 milliseconds to half a day)
on<RandomizeTime>((event, emit) {
final int milliseconds = random.nextInt(1000 * 60 * 60 * 12);
debugPrint('Generating time from milliseconds ($milliseconds)...');
emit(state
.changeDateTime(DateTime.fromMillisecondsSinceEpoch(milliseconds)));
debugPrint(
'Seconds: ${state.dateTime.second}, minutes: ${state.dateTime.minute}, hours: ${state.dateTime.hour}');
});
}
}
//-------------------------------Bloc-------------------------------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment