💄 Optimize designs and bug fixes with background image

This commit is contained in:
LittleSheep 2024-10-06 22:38:31 +08:00
parent c5258cb9ca
commit 2e9c4d166e
5 changed files with 320 additions and 304 deletions

View File

@ -108,7 +108,7 @@ class _AccountScreenState extends State<AccountScreen> {
child: ListView( child: ListView(
children: [ children: [
if (auth.userProfile.value != null) if (auth.userProfile.value != null)
const AccountHeading().paddingOnly(bottom: 8, top: 8), const AccountHeading().paddingOnly(bottom: 8, top: 16),
...(actionItems.map( ...(actionItems.map(
(x) => ListTile( (x) => ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 34), contentPadding: const EdgeInsets.symmetric(horizontal: 34),

View File

@ -220,284 +220,295 @@ class _SignInScreenState extends State<SignInScreen> {
return RootContainer( return RootContainer(
child: CenteredContainer( child: CenteredContainer(
maxWidth: 360, maxWidth: 360,
child: PageTransitionSwitcher( child: Theme(
transitionBuilder: ( data: Theme.of(context).copyWith(canvasColor: Colors.transparent),
Widget child, child: PageTransitionSwitcher(
Animation<double> primaryAnimation, transitionBuilder: (
Animation<double> secondaryAnimation, Widget child,
) { Animation<double> primaryAnimation,
return SharedAxisTransition( Animation<double> secondaryAnimation,
animation: primaryAnimation, ) {
secondaryAnimation: secondaryAnimation, return SharedAxisTransition(
transitionType: SharedAxisTransitionType.horizontal, animation: primaryAnimation,
child: child, secondaryAnimation: secondaryAnimation,
); transitionType: SharedAxisTransitionType.horizontal,
}, child: child,
child: switch (_period % 3) { );
1 => ListView( },
shrinkWrap: true, child: switch (_period % 3) {
key: const ValueKey<int>(1), 1 => ListView(
children: [ shrinkWrap: true,
Align( key: const ValueKey<int>(1),
alignment: Alignment.centerLeft, children: [
child: ClipRRect( Align(
borderRadius: const BorderRadius.all(Radius.circular(8)), alignment: Alignment.centerLeft,
child: child: ClipRRect(
Image.asset('assets/logo.png', width: 64, height: 64), borderRadius:
).paddingOnly(bottom: 8, left: 4), const BorderRadius.all(Radius.circular(8)),
), child: Image.asset('assets/logo.png',
Text( width: 64, height: 64),
'signinPickFactor'.tr, ).paddingOnly(bottom: 8, left: 4),
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w900,
), ),
).paddingOnly(left: 4, bottom: 16), Text(
Card( 'signinPickFactor'.tr,
margin: const EdgeInsets.symmetric(vertical: 4), style: const TextStyle(
child: Column( fontSize: 28,
children: _factors fontWeight: FontWeight.w900,
?.map(
(x) => CheckboxListTile(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(8),
),
),
secondary: Icon(
_factorLabelMap[x.type]?.$2 ??
Icons.question_mark,
),
title: Text(
_factorLabelMap[x.type]?.$1 ?? 'unknown'.tr,
),
enabled: !_currentTicket!.factorTrail
.contains(x.id),
value: _factorPicked == x.id,
onChanged: (value) {
if (value == true) {
setState(() => _factorPicked = x.id);
}
},
),
)
.toList() ??
List.empty(),
),
),
Text(
'signinMultiFactor'.trParams(
{'n': _currentTicket!.stepRemain.toString()},
),
style: TextStyle(color: _unFocusColor, fontSize: 12),
).paddingOnly(left: 16, right: 16),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: (_isBusy || _period > 1)
? null
: () => _previousStep(),
style:
TextButton.styleFrom(foregroundColor: Colors.grey),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.chevron_left),
Text('prev'.tr),
],
),
), ),
TextButton( ).paddingOnly(left: 4, bottom: 16),
onPressed: Card(
_isBusy ? null : () => _performGetFactorCode(), margin: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('next'.tr),
const Icon(Icons.chevron_right),
],
),
),
],
),
],
),
2 => ListView(
key: const ValueKey<int>(2),
shrinkWrap: true,
children: [
Align(
alignment: Alignment.centerLeft,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child:
Image.asset('assets/logo.png', width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text(
'signinEnterPassword'.tr,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w900,
),
).paddingOnly(left: 4, bottom: 16),
TextField(
autocorrect: false,
enableSuggestions: false,
controller: _passwordController,
obscureText: true,
autofillHints: [
(_factorLabelMap[_factorPickedType]?.$3 ?? true)
? AutofillHints.password
: AutofillHints.oneTimeCode
],
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText:
(_factorLabelMap[_factorPickedType]?.$3 ?? true)
? 'passwordOneTime'.tr
: 'password'.tr,
helperText:
(_factorLabelMap[_factorPickedType]?.$3 ?? true)
? 'passwordOneTimeInputHint'.tr
: 'passwordInputHint'.tr,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: _isBusy ? null : (_) => _performCheckTicket(),
),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: _isBusy ? null : () => _previousStep(),
style:
TextButton.styleFrom(foregroundColor: Colors.grey),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.chevron_left),
Text('prev'.tr),
],
),
),
TextButton(
onPressed: _isBusy ? null : () => _performCheckTicket(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('next'.tr),
const Icon(Icons.chevron_right),
],
),
),
],
),
],
),
_ => ListView(
key: const ValueKey<int>(0),
shrinkWrap: true,
children: [
Align(
alignment: Alignment.centerLeft,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child:
Image.asset('assets/logo.png', width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text(
'signinGreeting'.tr,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w900,
),
).paddingOnly(left: 4, bottom: 16),
TextField(
autocorrect: false,
enableSuggestions: false,
controller: _usernameController,
autofillHints: const [AutofillHints.username],
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: 'username'.tr,
helperText: 'usernameInputHint'.tr,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: _isBusy ? null : (_) => _performNewTicket(),
),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed:
_isBusy ? null : () => _requestResetPassword(),
style:
TextButton.styleFrom(foregroundColor: Colors.grey),
child: Text('forgotPassword'.tr),
),
TextButton(
onPressed: _isBusy ? null : () => _performNewTicket(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('next'.tr),
const Icon(Icons.chevron_right),
],
),
),
],
),
const Gap(12),
Align(
alignment: Alignment.centerRight,
child: Container(
constraints: const BoxConstraints(maxWidth: 290),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.end, children: _factors
children: [ ?.map(
Text( (x) => CheckboxListTile(
'termAcceptNextWithAgree'.tr, shape: const RoundedRectangleBorder(
textAlign: TextAlign.end, borderRadius: BorderRadius.all(
style: Radius.circular(8),
Theme.of(context).textTheme.bodySmall!.copyWith( ),
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.75),
), ),
), secondary: Icon(
Material( _factorLabelMap[x.type]?.$2 ??
color: Colors.transparent, Icons.question_mark,
child: InkWell( ),
child: Row( title: Text(
mainAxisSize: MainAxisSize.min, _factorLabelMap[x.type]?.$1 ??
children: [ 'unknown'.tr,
Text('termAcceptLink'.tr), ),
const Gap(4), enabled: !_currentTicket!.factorTrail
const Icon(Icons.launch, size: 14), .contains(x.id),
], value: _factorPicked == x.id,
), onChanged: (value) {
onTap: () { if (value == true) {
launchUrlString('https://solsynth.dev/terms'); setState(() => _factorPicked = x.id);
}, }
), },
), ),
], )
.toList() ??
List.empty(),
), ),
).paddingSymmetric(horizontal: 16), ),
), Text(
], 'signinMultiFactor'.trParams(
), {'n': _currentTicket!.stepRemain.toString()},
}, ),
style: TextStyle(color: _unFocusColor, fontSize: 12),
).paddingOnly(left: 16, right: 16),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: (_isBusy || _period > 1)
? null
: () => _previousStep(),
style: TextButton.styleFrom(
foregroundColor: Colors.grey),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.chevron_left),
Text('prev'.tr),
],
),
),
TextButton(
onPressed:
_isBusy ? null : () => _performGetFactorCode(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('next'.tr),
const Icon(Icons.chevron_right),
],
),
),
],
),
],
),
2 => ListView(
key: const ValueKey<int>(2),
shrinkWrap: true,
children: [
Align(
alignment: Alignment.centerLeft,
child: ClipRRect(
borderRadius:
const BorderRadius.all(Radius.circular(8)),
child: Image.asset('assets/logo.png',
width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text(
'signinEnterPassword'.tr,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w900,
),
).paddingOnly(left: 4, bottom: 16),
TextField(
autocorrect: false,
enableSuggestions: false,
controller: _passwordController,
obscureText: true,
autofillHints: [
(_factorLabelMap[_factorPickedType]?.$3 ?? true)
? AutofillHints.password
: AutofillHints.oneTimeCode
],
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText:
(_factorLabelMap[_factorPickedType]?.$3 ?? true)
? 'passwordOneTime'.tr
: 'password'.tr,
helperText:
(_factorLabelMap[_factorPickedType]?.$3 ?? true)
? 'passwordOneTimeInputHint'.tr
: 'passwordInputHint'.tr,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted:
_isBusy ? null : (_) => _performCheckTicket(),
),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: _isBusy ? null : () => _previousStep(),
style: TextButton.styleFrom(
foregroundColor: Colors.grey),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.chevron_left),
Text('prev'.tr),
],
),
),
TextButton(
onPressed:
_isBusy ? null : () => _performCheckTicket(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('next'.tr),
const Icon(Icons.chevron_right),
],
),
),
],
),
],
),
_ => ListView(
key: const ValueKey<int>(0),
shrinkWrap: true,
children: [
Align(
alignment: Alignment.centerLeft,
child: ClipRRect(
borderRadius:
const BorderRadius.all(Radius.circular(8)),
child: Image.asset('assets/logo.png',
width: 64, height: 64),
).paddingOnly(bottom: 8, left: 4),
),
Text(
'signinGreeting'.tr,
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.w900,
),
).paddingOnly(left: 4, bottom: 16),
TextField(
autocorrect: false,
enableSuggestions: false,
controller: _usernameController,
autofillHints: const [AutofillHints.username],
decoration: InputDecoration(
isDense: true,
border: const OutlineInputBorder(),
labelText: 'username'.tr,
helperText: 'usernameInputHint'.tr,
),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
onSubmitted: _isBusy ? null : (_) => _performNewTicket(),
),
const Gap(12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed:
_isBusy ? null : () => _requestResetPassword(),
style: TextButton.styleFrom(
foregroundColor: Colors.grey),
child: Text('forgotPassword'.tr),
),
TextButton(
onPressed: _isBusy ? null : () => _performNewTicket(),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('next'.tr),
const Icon(Icons.chevron_right),
],
),
),
],
),
const Gap(12),
Align(
alignment: Alignment.centerRight,
child: Container(
constraints: const BoxConstraints(maxWidth: 290),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'termAcceptNextWithAgree'.tr,
textAlign: TextAlign.end,
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.75),
),
),
Material(
color: Colors.transparent,
child: InkWell(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('termAcceptLink'.tr),
const Gap(4),
const Icon(Icons.launch, size: 14),
],
),
onTap: () {
launchUrlString('https://solsynth.dev/terms');
},
),
),
],
),
).paddingSymmetric(horizontal: 16),
),
],
),
},
),
), ),
).paddingAll(24), ).paddingAll(24),
); );

View File

@ -147,7 +147,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
), ),
Text(DateFormat('yyyy/MM/dd').format(DateTime.now().toUtc())), Text(DateFormat('yyyy/MM/dd').format(DateTime.now().toUtc())),
], ],
).paddingOnly(top: 8, left: 18, right: 18, bottom: 12), ).paddingOnly(top: 16, left: 18, right: 18, bottom: 12),
Card( Card(
child: Column( child: Column(
children: [ children: [

View File

@ -99,6 +99,7 @@ class _RealmListScreenState extends State<RealmListScreen> {
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () => _getRealms(), onRefresh: () => _getRealms(),
child: ListView.builder( child: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 16),
itemCount: _realms.length, itemCount: _realms.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final element = _realms[index]; final element = _realms[index];

View File

@ -27,39 +27,43 @@ class _AppNavigationRailState extends State<AppNavigationRail> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return NavigationRail( return Material(
selectedIndex: _currentIndex, color: Theme.of(context).colorScheme.surface,
labelType: NavigationRailLabelType.selected, child: NavigationRail(
groupAlignment: -1, selectedIndex: _currentIndex,
destinations: AppNavigation.destinations labelType: NavigationRailLabelType.selected,
.sublist(0, AppNavigation.destinations.length - 1) groupAlignment: -1,
.map( destinations: AppNavigation.destinations
(x) => NavigationRailDestination( .sublist(0, AppNavigation.destinations.length - 1)
icon: x.icon, .map(
label: Text(x.label), (x) => NavigationRailDestination(
icon: x.icon,
label: Text(x.label),
),
)
.toList(),
trailing: Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: IconButton(
icon: AppNavigation.destinations.last.icon,
tooltip: AppNavigation.destinations.last.label,
onPressed: () {
setState(() => _currentIndex = null);
AppRouter.instance
.goNamed(AppNavigation.destinations.last.page);
},
), ),
)
.toList(),
trailing: Expanded(
child: Align(
alignment: Alignment.bottomCenter,
child: IconButton(
icon: AppNavigation.destinations.last.icon,
tooltip: AppNavigation.destinations.last.label,
onPressed: () {
setState(() => _currentIndex = null);
AppRouter.instance.goNamed(AppNavigation.destinations.last.page);
},
), ),
), ),
onDestinationSelected: (idx) {
setState(() => _currentIndex = idx);
AppRouter.instance.goNamed(AppNavigation.destinations[idx].page);
},
).paddingOnly(
top: max(16, MediaQuery.of(context).padding.top),
bottom: max(16, MediaQuery.of(context).padding.bottom),
), ),
onDestinationSelected: (idx) {
setState(() => _currentIndex = idx);
AppRouter.instance.goNamed(AppNavigation.destinations[idx].page);
},
).paddingOnly(
top: max(16, MediaQuery.of(context).padding.top),
bottom: max(16, MediaQuery.of(context).padding.bottom),
); );
} }
} }