Preserve Flutter mirror-os codebase
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MirrorAnimations {
|
||||
MirrorAnimations._();
|
||||
|
||||
static const screenTransitionDuration = Duration(milliseconds: 800);
|
||||
static const quickTransitionDuration = Duration(milliseconds: 300);
|
||||
static const overlayFadeDuration = Duration(milliseconds: 500);
|
||||
|
||||
static Widget screenTransitionBuilder(
|
||||
Widget child,
|
||||
Animation<double> animation,
|
||||
) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
child: ScaleTransition(
|
||||
scale: Tween(begin: 0.95, end: 1.0).animate(
|
||||
CurvedAnimation(parent: animation, curve: Curves.easeOutCubic),
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Widget slideUpTransitionBuilder(
|
||||
Widget child,
|
||||
Animation<double> animation,
|
||||
) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
child: SlideTransition(
|
||||
position: Tween(
|
||||
begin: const Offset(0, 0.05),
|
||||
end: Offset.zero,
|
||||
).animate(CurvedAnimation(parent: animation, curve: Curves.easeOutCubic)),
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
library mirror_ui;
|
||||
|
||||
export 'theme.dart';
|
||||
export 'animations.dart';
|
||||
export 'widgets/progress_ring.dart';
|
||||
export 'widgets/timer_display.dart';
|
||||
@@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MirrorTheme {
|
||||
MirrorTheme._();
|
||||
|
||||
static const _zinc50 = Color(0xFFFAFAFA);
|
||||
static const _zinc100 = Color(0xFFF4F4F5);
|
||||
static const _zinc200 = Color(0xFFE4E4E7);
|
||||
static const _zinc400 = Color(0xFFA1A1AA);
|
||||
static const _zinc500 = Color(0xFF71717A);
|
||||
static const _zinc700 = Color(0xFF3F3F46);
|
||||
static const _zinc800 = Color(0xFF27272A);
|
||||
static const _zinc900 = Color(0xFF18181B);
|
||||
static const _zinc950 = Color(0xFF09090B);
|
||||
|
||||
static const _accent = Color(0xFF60A5FA); // blue-400
|
||||
static const _accentDim = Color(0xFF1E3A5F);
|
||||
static const _success = Color(0xFF4ADE80); // green-400
|
||||
static const _danger = Color(0xFFF87171); // red-400
|
||||
static const _warning = Color(0xFFFBBF24); // amber-400
|
||||
|
||||
static const fontFamily = 'Inter';
|
||||
static const fontMono = 'JetBrains Mono';
|
||||
|
||||
static ThemeData get dark => ThemeData(
|
||||
brightness: Brightness.dark,
|
||||
fontFamily: fontFamily,
|
||||
scaffoldBackgroundColor: _zinc950,
|
||||
colorScheme: const ColorScheme.dark(
|
||||
surface: _zinc900,
|
||||
primary: _accent,
|
||||
secondary: _zinc700,
|
||||
error: _danger,
|
||||
onSurface: _zinc100,
|
||||
onPrimary: _zinc950,
|
||||
),
|
||||
cardTheme: const CardThemeData(
|
||||
color: _zinc900,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
fontSize: 96,
|
||||
fontWeight: FontWeight.w200,
|
||||
fontFamily: fontMono,
|
||||
color: _zinc50,
|
||||
letterSpacing: -2,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
fontSize: 60,
|
||||
fontWeight: FontWeight.w200,
|
||||
fontFamily: fontMono,
|
||||
color: _zinc50,
|
||||
),
|
||||
displaySmall: TextStyle(
|
||||
fontSize: 36,
|
||||
fontWeight: FontWeight.w300,
|
||||
color: _zinc50,
|
||||
),
|
||||
headlineLarge: TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _zinc50,
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: _zinc50,
|
||||
),
|
||||
bodyLarge: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: _zinc200,
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: _zinc400,
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: _zinc500,
|
||||
),
|
||||
labelLarge: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: _zinc50,
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
iconTheme: const IconThemeData(color: _zinc400, size: 24),
|
||||
dividerColor: _zinc800,
|
||||
);
|
||||
|
||||
static const Color accent = _accent;
|
||||
static const Color accentDim = _accentDim;
|
||||
static const Color success = _success;
|
||||
static const Color danger = _danger;
|
||||
static const Color warning = _warning;
|
||||
static const Color surface = _zinc900;
|
||||
static const Color surfaceLight = _zinc800;
|
||||
static const Color background = _zinc950;
|
||||
static const Color textPrimary = _zinc50;
|
||||
static const Color textSecondary = _zinc400;
|
||||
static const Color textMuted = _zinc500;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import 'dart:math' as math;
|
||||
import 'package:flutter/material.dart';
|
||||
import '../theme.dart';
|
||||
|
||||
class ProgressRing extends StatelessWidget {
|
||||
final double percent;
|
||||
final double size;
|
||||
final double strokeWidth;
|
||||
final Color? color;
|
||||
final Color? backgroundColor;
|
||||
|
||||
const ProgressRing({
|
||||
super.key,
|
||||
required this.percent,
|
||||
this.size = 36,
|
||||
this.strokeWidth = 3,
|
||||
this.color,
|
||||
this.backgroundColor,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final effectiveColor = percent >= 95
|
||||
? MirrorTheme.success
|
||||
: (color ?? MirrorTheme.accent);
|
||||
|
||||
return SizedBox(
|
||||
width: size,
|
||||
height: size,
|
||||
child: CustomPaint(
|
||||
painter: _ProgressRingPainter(
|
||||
percent: percent.clamp(0, 100),
|
||||
strokeWidth: strokeWidth,
|
||||
color: effectiveColor,
|
||||
backgroundColor: backgroundColor ?? MirrorTheme.surfaceLight,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ProgressRingPainter extends CustomPainter {
|
||||
final double percent;
|
||||
final double strokeWidth;
|
||||
final Color color;
|
||||
final Color backgroundColor;
|
||||
|
||||
_ProgressRingPainter({
|
||||
required this.percent,
|
||||
required this.strokeWidth,
|
||||
required this.color,
|
||||
required this.backgroundColor,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final center = Offset(size.width / 2, size.height / 2);
|
||||
final radius = (size.width - strokeWidth) / 2;
|
||||
|
||||
final bgPaint = Paint()
|
||||
..color = backgroundColor
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth;
|
||||
|
||||
final fgPaint = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
canvas.drawCircle(center, radius, bgPaint);
|
||||
|
||||
final sweepAngle = (percent / 100) * 2 * math.pi;
|
||||
canvas.drawArc(
|
||||
Rect.fromCircle(center: center, radius: radius),
|
||||
-math.pi / 2,
|
||||
sweepAngle,
|
||||
false,
|
||||
fgPaint,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_ProgressRingPainter oldDelegate) =>
|
||||
oldDelegate.percent != percent || oldDelegate.color != color;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../theme.dart';
|
||||
|
||||
class TimerDisplay extends StatelessWidget {
|
||||
final int seconds;
|
||||
final String? label;
|
||||
final TextStyle? style;
|
||||
|
||||
const TimerDisplay({
|
||||
super.key,
|
||||
required this.seconds,
|
||||
this.label,
|
||||
this.style,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final minutes = seconds ~/ 60;
|
||||
final secs = seconds % 60;
|
||||
final timeText = minutes > 0
|
||||
? '$minutes:${secs.toString().padLeft(2, '0')}'
|
||||
: '$secs';
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (label != null)
|
||||
Text(
|
||||
label!,
|
||||
style: Theme.of(context).textTheme.labelLarge?.copyWith(
|
||||
color: MirrorTheme.accent,
|
||||
letterSpacing: 8,
|
||||
),
|
||||
),
|
||||
if (label != null) const SizedBox(height: 40),
|
||||
Text(
|
||||
timeText,
|
||||
style: style ?? Theme.of(context).textTheme.displayLarge?.copyWith(
|
||||
color: MirrorTheme.accent,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
name: mirror_ui
|
||||
description: Shared theme, animations, and common widgets for Mirror OS
|
||||
version: 0.1.0
|
||||
publish_to: none
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.0
|
||||
flutter: ^3.24.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
Reference in New Issue
Block a user