diff --git a/lib/home/bloc/home_state.dart b/lib/home/bloc/home_state.dart index 558b0e8..a131bef 100644 --- a/lib/home/bloc/home_state.dart +++ b/lib/home/bloc/home_state.dart @@ -29,6 +29,14 @@ class HomeState extends Equatable { status == Status.askQuestionToThinking || status == Status.thinking; bool get isResultsVisible => status == Status.thinkingToResults || status == Status.results; + bool get isDashVisible => [ + Status.welcome, + Status.welcomeToAskQuestion, + Status.askQuestion, + Status.askQuestionToThinking, + Status.thinkingToResults, + Status.results, + ].contains(status); HomeState copyWith({ Status? status, diff --git a/lib/home/view/home_page.dart b/lib/home/view/home_page.dart index 0b0fbcb..f8b7715 100644 --- a/lib/home/view/home_page.dart +++ b/lib/home/view/home_page.dart @@ -28,11 +28,12 @@ class HomeView extends StatelessWidget { backgroundColor: VertexColors.arctic, body: Stack( children: [ - const Positioned( - top: 0, - bottom: 0, - child: Background(), - ), + if (state.isWelcomeVisible) + const Positioned( + top: 0, + bottom: 0, + child: Background(), + ), const Positioned( top: 40, left: 48, @@ -42,11 +43,12 @@ class HomeView extends StatelessWidget { if (state.isQuestionVisible) const QuestionView(), if (state.isThinkingVisible) const ThinkingView(), if (state.isResultsVisible) const ResultsView(), - const Positioned( - bottom: 50, - left: 50, - child: DashAnimation(), - ), + if (state.isDashVisible) + const Positioned( + bottom: 50, + left: 50, + child: DashAnimation(), + ), ], ), ); diff --git a/lib/home/widgets/background.dart b/lib/home/widgets/background.dart index b8af058..ef09c3c 100644 --- a/lib/home/widgets/background.dart +++ b/lib/home/widgets/background.dart @@ -1,6 +1,6 @@ import 'package:app_ui/app_ui.dart'; +import 'package:dash_ai_search/home/home.dart'; import 'package:flutter/material.dart'; -import 'package:path_drawing/path_drawing.dart'; class Background extends StatelessWidget { const Background({super.key}); @@ -45,98 +45,3 @@ class Background extends StatelessWidget { ); } } - -class Circle extends StatelessWidget { - @visibleForTesting - const Circle({ - this.offset = Offset.zero, - this.radius = 303, - this.borderColor = VertexColors.white, - this.dotted = false, - this.child, - super.key, - }); - - final Offset offset; - final double radius; - final Color borderColor; - final bool dotted; - final Widget? child; - - @override - Widget build(BuildContext context) { - return CustomPaint( - painter: CirclePainter( - offset: offset, - radius: radius, - borderColor: borderColor, - dotted: dotted, - ), - child: child, - ); - } -} - -class CirclePainter extends CustomPainter { - CirclePainter({ - required this.offset, - required this.radius, - required this.borderColor, - required this.dotted, - }) { - _paintCircle = Paint() - ..color = VertexColors.white - ..style = PaintingStyle.fill; - _paintBorder = Paint() - ..color = borderColor - ..strokeWidth = 2 - ..strokeCap = StrokeCap.butt - ..style = PaintingStyle.stroke; - } - - final Offset offset; - final double radius; - final Color borderColor; - final bool dotted; - - late final Paint _paintCircle; - late final Paint _paintBorder; - - @override - void paint(Canvas canvas, Size size) { - canvas.drawCircle( - offset, - radius, - _paintCircle, - ); - - if (dotted) { - const dashPattern = [4, 4]; - final s = radius * 2; - - var path = Path() - ..addRRect( - RRect.fromRectAndRadius( - Rect.fromLTWH( - offset.dx - s / 2, - offset.dy / 2 - s / 2, - s, - s, - ), - Radius.circular(s / 2), - ), - ); - path = dashPath(path, dashArray: CircularIntervalList(dashPattern)); - canvas.drawPath(path, _paintBorder); - } else { - canvas.drawCircle( - offset, - radius, - _paintBorder, - ); - } - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; -} diff --git a/lib/home/widgets/circle.dart b/lib/home/widgets/circle.dart new file mode 100644 index 0000000..14f27db --- /dev/null +++ b/lib/home/widgets/circle.dart @@ -0,0 +1,103 @@ +import 'package:app_ui/app_ui.dart'; +import 'package:flutter/widgets.dart'; +import 'package:path_drawing/path_drawing.dart'; + +class Circle extends StatelessWidget { + const Circle({ + this.offset = Offset.zero, + this.radius = 303, + this.borderColor = VertexColors.white, + this.dotted = false, + this.child, + this.backgroundColor = VertexColors.white, + super.key, + }); + + final Offset offset; + final double radius; + final Color borderColor; + final bool dotted; + final Widget? child; + final Color backgroundColor; + + @override + Widget build(BuildContext context) { + return CustomPaint( + painter: CirclePainter( + offset: offset, + radius: radius, + borderColor: borderColor, + dotted: dotted, + backgroundColor: backgroundColor, + ), + child: child, + ); + } +} + +class CirclePainter extends CustomPainter { + @visibleForTesting + CirclePainter({ + required this.offset, + required this.radius, + required this.borderColor, + required this.dotted, + required this.backgroundColor, + }) { + _paintCircle = Paint() + ..color = backgroundColor + ..style = PaintingStyle.fill; + _paintBorder = Paint() + ..color = borderColor + ..strokeWidth = 2 + ..strokeCap = StrokeCap.butt + ..style = PaintingStyle.stroke; + } + + final Offset offset; + final double radius; + final Color borderColor; + final bool dotted; + final Color backgroundColor; + + late final Paint _paintCircle; + late final Paint _paintBorder; + + @override + void paint(Canvas canvas, Size size) { + canvas.drawCircle( + offset, + radius, + _paintCircle, + ); + + if (dotted) { + const dashPattern = [4, 4]; + final s = radius * 2; + + var path = Path() + ..addRRect( + RRect.fromRectAndRadius( + Rect.fromLTWH( + offset.dx - s / 2, + offset.dy / 2 - s / 2, + s, + s, + ), + Radius.circular(s / 2), + ), + ); + path = dashPath(path, dashArray: CircularIntervalList(dashPattern)); + canvas.drawPath(path, _paintBorder); + } else { + canvas.drawCircle( + offset, + radius, + _paintBorder, + ); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => false; +} diff --git a/lib/home/widgets/thinking_view.dart b/lib/home/widgets/thinking_view.dart index 21a954e..9b3fe3c 100644 --- a/lib/home/widgets/thinking_view.dart +++ b/lib/home/widgets/thinking_view.dart @@ -1,3 +1,4 @@ +import 'package:app_ui/app_ui.dart'; import 'package:dash_ai_search/home/home.dart'; import 'package:dash_ai_search/l10n/l10n.dart'; import 'package:flutter/material.dart'; @@ -8,34 +9,88 @@ class ThinkingView extends StatelessWidget { @override Widget build(BuildContext context) { - final theme = Theme.of(context); - final l10n = context.l10n; - - final state = context.watch().state; - final isAnimating = state.status == Status.askQuestionToThinking; + final isAnimating = context.select( + (HomeBloc bloc) => bloc.state.status == Status.askQuestionToThinking, + ); final query = context.select((HomeBloc bloc) => bloc.state.query); return AnimatedOpacity( opacity: isAnimating ? 1 : 0, duration: const Duration(milliseconds: 500), - child: Center( - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - l10n.thinkingHeadline, - textAlign: TextAlign.center, - style: theme.textTheme.bodyMedium, - ), - Text( - query, - textAlign: TextAlign.center, - style: theme.textTheme.headlineLarge, - ), - ], + child: Stack( + children: [ + const Align(child: Circles()), + Align( + child: TextArea(query: query), ), - ), + ], ), ); } } + +class TextArea extends StatelessWidget { + @visibleForTesting + const TextArea({required this.query, super.key}); + + final String query; + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + l10n.thinkingHeadline, + textAlign: TextAlign.center, + style: + VertexTextStyles.body.copyWith(color: VertexColors.flutterNavy), + ), + const SizedBox( + height: 40, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 300), + child: Text( + query, + textAlign: TextAlign.center, + style: VertexTextStyles.display, + ), + ), + ], + ); + } +} + +class Circles extends StatelessWidget { + const Circles({super.key}); + + @override + Widget build(BuildContext context) { + const backgroundColor = Colors.transparent; + const borderColor = VertexColors.googleBlue; + return const Stack( + children: [ + Circle( + dotted: true, + backgroundColor: backgroundColor, + borderColor: borderColor, + radius: 162, + ), + Circle( + dotted: true, + backgroundColor: backgroundColor, + borderColor: borderColor, + radius: 353, + ), + Circle( + dotted: true, + backgroundColor: backgroundColor, + borderColor: borderColor, + radius: 590, + ), + ], + ); + } +} diff --git a/lib/home/widgets/widgets.dart b/lib/home/widgets/widgets.dart index 14803b2..69f719a 100644 --- a/lib/home/widgets/widgets.dart +++ b/lib/home/widgets/widgets.dart @@ -1,4 +1,5 @@ export 'background.dart'; +export 'circle.dart'; export 'logo.dart'; export 'question_view.dart'; export 'results_view.dart'; diff --git a/packages/app_ui/lib/src/colors/vertex_colors.dart b/packages/app_ui/lib/src/colors/vertex_colors.dart index 05f3e39..37ca305 100644 --- a/packages/app_ui/lib/src/colors/vertex_colors.dart +++ b/packages/app_ui/lib/src/colors/vertex_colors.dart @@ -5,6 +5,9 @@ abstract class VertexColors { /// Navy Color Swatch static const Color navy = Color(0xff020f30); + /// Fluter Navy Color Swatch + static const Color flutterNavy = Color(0xff042A59); + /// Google Blue Color Swatch static const Color googleBlue = Color(0xff3089f1); diff --git a/test/home/widgets/background_test.dart b/test/home/widgets/background_test.dart index cdc446f..90a2c57 100644 --- a/test/home/widgets/background_test.dart +++ b/test/home/widgets/background_test.dart @@ -20,6 +20,7 @@ void main() { radius: 10, borderColor: Colors.white, dotted: false, + backgroundColor: Colors.white, ); expect(circlePainter.shouldRepaint(circlePainter), false); });