WIP: feat(ui): Migrate to new UI implementation
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_launcher_components.dart';
|
||||
import 'package:moonwell_launcher/app/design_system/mw_window_chrome.dart';
|
||||
import 'package:moonwell_launcher/app/theme/moonwell_design_system.dart';
|
||||
|
||||
const _ink = Color(0xFF06091A);
|
||||
const _ivory = Color(0xFFECE4D0);
|
||||
|
||||
class MwBrandMark extends StatelessWidget {
|
||||
const MwBrandMark({super.key, this.width = 220, this.compact = false});
|
||||
|
||||
final double width;
|
||||
final bool compact;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accent = MoonWellDesignTokens.of(context).accent;
|
||||
return Semantics(
|
||||
image: true,
|
||||
label: 'MoonWell',
|
||||
child: SizedBox(
|
||||
width: width,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_MoonMark(size: compact ? 28 : 40, color: accent),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'MOONWELL',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Cinzel',
|
||||
color: _ivory,
|
||||
fontSize: compact ? 15 : 22,
|
||||
height: 1,
|
||||
letterSpacing: compact ? 4 : 6,
|
||||
),
|
||||
),
|
||||
if (!compact) ...[
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
'LUNAR · LAUNCHER',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: accent,
|
||||
fontSize: 9,
|
||||
letterSpacing: 2.6,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MoonMark extends StatelessWidget {
|
||||
const _MoonMark({required this.size, required this.color});
|
||||
|
||||
final double size;
|
||||
final Color color;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox.square(
|
||||
dimension: size,
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: color.withAlpha(90)),
|
||||
boxShadow: [BoxShadow(color: color.withAlpha(70), blurRadius: 16)],
|
||||
),
|
||||
child: Icon(Icons.nightlight_round, color: color, size: size * .72),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MwArtworkBackground extends StatelessWidget {
|
||||
const MwArtworkBackground({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.assetPackage,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
final String? assetPackage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accent = MoonWellDesignTokens.of(context).accent;
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
const ColoredBox(color: _ink),
|
||||
// Image.asset(
|
||||
// 'assets/background.png',
|
||||
// package: assetPackage,
|
||||
// fit: BoxFit.cover,
|
||||
// alignment: Alignment.center,
|
||||
// errorBuilder: (_, _, _) => const SizedBox.shrink(),
|
||||
// ),
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.centerLeft,
|
||||
end: Alignment.centerRight,
|
||||
colors: [
|
||||
_ink.withAlpha(235),
|
||||
_ink.withAlpha(85),
|
||||
_ink.withAlpha(165),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
gradient: RadialGradient(
|
||||
center: const Alignment(.35, -.5),
|
||||
radius: .8,
|
||||
colors: [accent.withAlpha(42), Colors.transparent],
|
||||
),
|
||||
),
|
||||
),
|
||||
child,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MwLoginShell extends StatelessWidget {
|
||||
const MwLoginShell({
|
||||
super.key,
|
||||
required this.form,
|
||||
required this.onDrag,
|
||||
required this.onDoubleTap,
|
||||
required this.onMinimize,
|
||||
required this.onMaximizeRestore,
|
||||
required this.onClose,
|
||||
this.assetPackage,
|
||||
});
|
||||
|
||||
final Widget form;
|
||||
final VoidCallback onDrag;
|
||||
final VoidCallback onDoubleTap;
|
||||
final VoidCallback onMinimize;
|
||||
final VoidCallback onMaximizeRestore;
|
||||
final VoidCallback onClose;
|
||||
final String? assetPackage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: MwArtworkBackground(
|
||||
assetPackage: assetPackage,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(64, 80, 64, 58),
|
||||
child: Align(
|
||||
alignment: Alignment.bottomLeft,
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 500),
|
||||
child: const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'✦',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF7FB8D4),
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
'«Звёздный свет проведёт тебя сквозь самые '
|
||||
'тёмные ночи — если ты готов идти туда, '
|
||||
'куда он указывает.»',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Cormorant Garamond',
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Color(0xC6ECE4D0),
|
||||
fontSize: 22,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
Text(
|
||||
'— МАЛФУРИОН ЯРОСТЬ БУРИ',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: Color(0x80ECE4D0),
|
||||
fontSize: 10,
|
||||
letterSpacing: 2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 480,
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xF20A0D1F),
|
||||
border: Border(
|
||||
left: BorderSide(color: Color(0x247FB8D4)),
|
||||
),
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.fromLTRB(56, 64, 56, 58),
|
||||
child: form,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: MwWindowChrome(
|
||||
onDrag: onDrag,
|
||||
onDoubleTap: onDoubleTap,
|
||||
onMinimize: onMinimize,
|
||||
onMaximizeRestore: onMaximizeRestore,
|
||||
onClose: onClose,
|
||||
showMaximize: false,
|
||||
),
|
||||
),
|
||||
const Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: 26,
|
||||
child: _LoginStatusBar(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginStatusBar extends StatelessWidget {
|
||||
const _LoginStatusBar();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xD904060E),
|
||||
border: Border(top: BorderSide(color: Color(0x247FB8D4))),
|
||||
),
|
||||
child: const Row(
|
||||
children: [
|
||||
Text('Версия лаунчера 1.0.0'),
|
||||
SizedBox(width: 24),
|
||||
Text('·'),
|
||||
SizedBox(width: 24),
|
||||
Text('Соединение защищено TLS'),
|
||||
Spacer(),
|
||||
Text('© 2026 MoonWell'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MwLauncherShell extends StatelessWidget {
|
||||
const MwLauncherShell({
|
||||
super.key,
|
||||
required this.news,
|
||||
required this.syncActions,
|
||||
required this.statusBar,
|
||||
required this.account,
|
||||
required this.onDrag,
|
||||
required this.onDoubleTap,
|
||||
required this.onMinimize,
|
||||
required this.onMaximizeRestore,
|
||||
required this.onClose,
|
||||
this.assetPackage,
|
||||
});
|
||||
|
||||
final Widget news;
|
||||
final Widget syncActions;
|
||||
final Widget statusBar;
|
||||
final Widget account;
|
||||
final VoidCallback onDrag;
|
||||
final VoidCallback onDoubleTap;
|
||||
final VoidCallback onMinimize;
|
||||
final VoidCallback onMaximizeRestore;
|
||||
final VoidCallback onClose;
|
||||
final String? assetPackage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: MwArtworkBackground(
|
||||
assetPackage: assetPackage,
|
||||
child: Stack(
|
||||
children: [
|
||||
const Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: 240,
|
||||
child: MwGameRail(),
|
||||
),
|
||||
Positioned(
|
||||
left: 272,
|
||||
top: 24,
|
||||
child: const MwBrandMark(width: 245),
|
||||
),
|
||||
Positioned(right: 28, top: 48, child: account),
|
||||
const Positioned(left: 272, top: 160, child: _HeroCopy()),
|
||||
Positioned(
|
||||
right: 24,
|
||||
top: 110,
|
||||
bottom: 130,
|
||||
width: 360,
|
||||
child: news,
|
||||
),
|
||||
Positioned(
|
||||
left: 240,
|
||||
right: 0,
|
||||
bottom: 26,
|
||||
height: 110,
|
||||
child: syncActions,
|
||||
),
|
||||
Positioned(
|
||||
left: 240,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: 26,
|
||||
child: statusBar,
|
||||
),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: MwWindowChrome(
|
||||
onDrag: onDrag,
|
||||
onDoubleTap: onDoubleTap,
|
||||
onMinimize: onMinimize,
|
||||
onMaximizeRestore: onMaximizeRestore,
|
||||
onClose: onClose,
|
||||
showMaximize: false,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _HeroCopy extends StatelessWidget {
|
||||
const _HeroCopy();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accent = MoonWellDesignTokens.of(context).accent;
|
||||
return SizedBox(
|
||||
width: 520,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: accent),
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 6,
|
||||
height: 6,
|
||||
decoration: BoxDecoration(
|
||||
color: accent,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
'OPEN BETA',
|
||||
style: TextStyle(
|
||||
fontFamily: 'JetBrains Mono',
|
||||
color: accent,
|
||||
fontSize: 11,
|
||||
letterSpacing: 2.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
const Text(
|
||||
'Свет колодца',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Cinzel',
|
||||
color: _ivory,
|
||||
fontSize: 50,
|
||||
height: 1.05,
|
||||
letterSpacing: 1.5,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'зовёт тебя домой.',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Cormorant Garamond',
|
||||
fontStyle: FontStyle.italic,
|
||||
color: accent,
|
||||
fontSize: 54,
|
||||
height: 1.05,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
const Text(
|
||||
'Wrath of the Lich King · приватный сервер',
|
||||
style: TextStyle(
|
||||
fontFamily: 'Cormorant Garamond',
|
||||
fontStyle: FontStyle.italic,
|
||||
color: Color(0xC6ECE4D0),
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user