💄 Finishing up refactor background image related changes

This commit is contained in:
LittleSheep 2025-01-21 20:50:07 +08:00
parent c82dc7ad85
commit 127d9adc09
8 changed files with 266 additions and 276 deletions

View File

@ -282,7 +282,7 @@ final _appRoutes = [
), ),
), ),
ShellRoute( ShellRoute(
builder: (context, state, child) => AppPageScaffold(body: child), builder: (context, state, child) => child,
routes: [ routes: [
GoRoute( GoRoute(
path: '/settings', path: '/settings',
@ -292,7 +292,7 @@ final _appRoutes = [
], ],
), ),
ShellRoute( ShellRoute(
builder: (context, state, child) => AppPageScaffold(body: child), builder: (context, state, child) => child,
routes: [ routes: [
GoRoute( GoRoute(
path: '/about', path: '/about',

View File

@ -210,7 +210,7 @@ class _ProfileEditScreenState extends State<ProfileEditScreen> {
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
leading: const PageBackButton(), leading: const PageBackButton(),
title: Text('screenProfileEdit').tr(), title: Text('screenAccountProfileEdit').tr(),
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Column( child: Column(

View File

@ -234,52 +234,56 @@ class _FriendScreenState extends State<FriendScreen> {
if (_requests.isNotEmpty || _blocks.isNotEmpty) if (_requests.isNotEmpty || _blocks.isNotEmpty)
const Divider(height: 1), const Divider(height: 1),
Expanded( Expanded(
child: RefreshIndicator( child: MediaQuery.removePadding(
onRefresh: () => Future.wait([ context: context,
_fetchRelations(), removeTop: true,
_fetchRequests(), child: RefreshIndicator(
]), onRefresh: () => Future.wait([
child: ListView.builder( _fetchRelations(),
itemCount: _relations.length, _fetchRequests(),
itemBuilder: (context, index) { ]),
final relation = _relations[index]; child: ListView.builder(
final other = relation.related; itemCount: _relations.length,
return ListTile( itemBuilder: (context, index) {
contentPadding: const EdgeInsets.only(right: 24, left: 16), final relation = _relations[index];
leading: AccountImage(content: other?.avatar), final other = relation.related;
title: Text(other?.nick ?? 'unknown'), return ListTile(
subtitle: Text(other?.nick ?? 'unknown'), contentPadding: const EdgeInsets.only(right: 24, left: 16),
trailing: SizedBox( leading: AccountImage(content: other?.avatar),
height: 48, title: Text(other?.nick ?? 'unknown'),
width: 120, subtitle: Text(other?.nick ?? 'unknown'),
child: Column( trailing: SizedBox(
mainAxisSize: MainAxisSize.min, height: 48,
mainAxisAlignment: MainAxisAlignment.center, width: 120,
crossAxisAlignment: CrossAxisAlignment.end, child: Column(
children: [ mainAxisSize: MainAxisSize.min,
Row( mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
InkWell( Row(
onTap: _isUpdating mainAxisAlignment: MainAxisAlignment.end,
? null children: [
: () => _changeRelation(relation, 2), InkWell(
child: Text('friendBlock').tr(), onTap: _isUpdating
), ? null
const Gap(8), : () => _changeRelation(relation, 2),
InkWell( child: Text('friendBlock').tr(),
onTap: _isUpdating ),
? null const Gap(8),
: () => _deleteRelation(relation), InkWell(
child: Text('friendDeleteAction').tr(), onTap: _isUpdating
), ? null
], : () => _deleteRelation(relation),
), child: Text('friendDeleteAction').tr(),
], ),
],
),
],
),
), ),
), );
); },
}, ),
), ),
), ),
), ),

View File

@ -119,113 +119,61 @@ class _RealmScreenState extends State<RealmScreen> {
children: [ children: [
LoadingIndicator(isActive: _isBusy), LoadingIndicator(isActive: _isBusy),
Expanded( Expanded(
child: RefreshIndicator( child: MediaQuery.removePadding(
onRefresh: _fetchRealms, context: context,
child: ListView.builder( removeTop: true,
itemCount: _realms?.length ?? 0, child: RefreshIndicator(
itemBuilder: (context, idx) { onRefresh: _fetchRealms,
final realm = _realms![idx]; child: ListView.builder(
if (_isCompactView) { itemCount: _realms?.length ?? 0,
return ListTile( itemBuilder: (context, idx) {
contentPadding: const EdgeInsets.symmetric(horizontal: 16), final realm = _realms![idx];
leading: AccountImage( if (_isCompactView) {
content: realm.avatar, return ListTile(
fallbackWidget: const Icon(Symbols.group, size: 20), contentPadding: const EdgeInsets.symmetric(horizontal: 16),
), leading: AccountImage(
title: Text(realm.name), content: realm.avatar,
subtitle: Text( fallbackWidget: const Icon(Symbols.group, size: 20),
realm.description, ),
maxLines: 1, title: Text(realm.name),
overflow: TextOverflow.ellipsis, subtitle: Text(
), realm.description,
trailing: PopupMenuButton( maxLines: 1,
itemBuilder: (BuildContext context) => [ overflow: TextOverflow.ellipsis,
PopupMenuItem( ),
child: Row( trailing: PopupMenuButton(
children: [ itemBuilder: (BuildContext context) => [
const Icon(Symbols.edit), PopupMenuItem(
const Gap(16), child: Row(
Text('edit').tr(),
],
),
onTap: () {
GoRouter.of(context).pushNamed(
'realmManage',
queryParameters: {'editing': realm.alias},
).then((value) {
if (value != null) {
_fetchRealms();
}
});
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.delete),
const Gap(16),
Text('delete').tr(),
],
),
onTap: () {
_deleteRealm(realm);
},
),
],
),
onTap: () {
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'alias': realm.alias},
);
},
);
}
return Container(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
margin: const EdgeInsets.all(12),
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [ children: [
Container( const Icon(Symbols.edit),
color: Theme.of(context).colorScheme.surfaceContainer, const Gap(16),
child: (realm.banner?.isEmpty ?? true) Text('edit').tr(),
? const SizedBox.shrink()
: AutoResizeUniversalImage(
sn.getAttachmentUrl(realm.banner!),
fit: BoxFit.cover,
),
),
Positioned(
bottom: -30,
left: 18,
child: AccountImage(
content: realm.avatar,
radius: 24,
fallbackWidget: const Icon(Symbols.group, size: 24),
),
),
], ],
), ),
onTap: () {
GoRouter.of(context).pushNamed(
'realmManage',
queryParameters: {'editing': realm.alias},
).then((value) {
if (value != null) {
_fetchRealms();
}
});
},
),
PopupMenuItem(
child: Row(
children: [
const Icon(Symbols.delete),
const Gap(16),
Text('delete').tr(),
],
),
onTap: () {
_deleteRealm(realm);
},
), ),
const Gap(20 + 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(realm.name).textStyle(Theme.of(context).textTheme.titleMedium!),
Text(realm.description).textStyle(Theme.of(context).textTheme.bodySmall!),
],
).padding(horizontal: 24, bottom: 14),
], ],
), ),
onTap: () { onTap: () {
@ -234,10 +182,69 @@ class _RealmScreenState extends State<RealmScreen> {
pathParameters: {'alias': realm.alias}, pathParameters: {'alias': realm.alias},
); );
}, },
);
}
return Container(
constraints: BoxConstraints(maxWidth: 640),
child: Card(
margin: const EdgeInsets.all(12),
child: InkWell(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 7,
child: Stack(
clipBehavior: Clip.none,
fit: StackFit.expand,
children: [
ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: (realm.banner?.isEmpty ?? true)
? const SizedBox.shrink()
: AutoResizeUniversalImage(
sn.getAttachmentUrl(realm.banner!),
fit: BoxFit.cover,
),
),
),
Positioned(
bottom: -30,
left: 18,
child: AccountImage(
content: realm.avatar,
radius: 24,
fallbackWidget: const Icon(Symbols.group, size: 24),
),
),
],
),
),
const Gap(20 + 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(realm.name).textStyle(Theme.of(context).textTheme.titleMedium!),
Text(realm.description).textStyle(Theme.of(context).textTheme.bodySmall!),
],
).padding(horizontal: 24, bottom: 14),
],
),
onTap: () {
GoRouter.of(context).pushNamed(
'realmDetail',
pathParameters: {'alias': realm.alias},
);
},
),
), ),
), ).center();
).center(); },
}, ),
), ),
), ),
), ),

View File

@ -8,14 +8,13 @@ import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/sn_network.dart';
import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/user_directory.dart';
import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/userinfo.dart';
import 'package:surface/types/post.dart';
import 'package:surface/types/realm.dart'; import 'package:surface/types/realm.dart';
import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/account/account_image.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:very_good_infinite_list/very_good_infinite_list.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart';
import '../../types/post.dart';
class RealmDetailScreen extends StatefulWidget { class RealmDetailScreen extends StatefulWidget {
final String alias; final String alias;
@ -421,7 +420,7 @@ class _RealmSettingsWidgetState extends State<_RealmSettingsWidget> {
return Column( return Column(
children: [ children: [
const Gap(16), const Gap(8),
ListTile( ListTile(
leading: const Icon(Symbols.edit), leading: const Icon(Symbols.edit),
trailing: const Icon(Symbols.chevron_right), trailing: const Icon(Symbols.chevron_right),

View File

@ -68,8 +68,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sn = context.read<SnNetworkProvider>(); final sn = context.read<SnNetworkProvider>();
return Scaffold( return AppScaffold(
backgroundColor: Colors.transparent, appBar: AppBar(
leading: const PageBackButton(),
title: Text('screenSettings').tr(),
),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Column( child: Column(
spacing: 16, spacing: 16,

View File

@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
class AboutScreen extends StatelessWidget { class AboutScreen extends StatelessWidget {
@ -12,97 +13,103 @@ class AboutScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
const denseButtonStyle = ButtonStyle(visualDensity: VisualDensity(vertical: -4)); const denseButtonStyle = ButtonStyle(visualDensity: VisualDensity(vertical: -4));
return SizedBox( return AppScaffold(
width: double.infinity, appBar: AppBar(
child: Column( leading: const PageBackButton(),
mainAxisAlignment: MainAxisAlignment.center, title: Text('screenAbout').tr(),
children: [ ),
ClipRRect( body: SizedBox(
borderRadius: const BorderRadius.all(Radius.circular(16)), width: double.infinity,
child: Image.asset('assets/icon/icon-light-radius.png', width: 120, height: 120), child: Column(
), mainAxisAlignment: MainAxisAlignment.center,
const Gap(8), children: [
Text( ClipRRect(
'Solian', borderRadius: const BorderRadius.all(Radius.circular(16)),
style: Theme.of(context).textTheme.titleLarge!.copyWith(fontSize: 36), child: Image.asset('assets/icon/icon-light-radius.png', width: 120, height: 120),
), ),
const Text( const Gap(8),
'The Solar Network', Text(
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), 'Solian',
), style: Theme.of(context).textTheme.titleLarge!.copyWith(fontSize: 36),
const Gap(8), ),
FutureBuilder( const Text(
future: PackageInfo.fromPlatform(), 'The Solar Network',
builder: (context, snapshot) { style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
if (!snapshot.hasData) { ),
return const SizedBox.shrink(); const Gap(8),
} FutureBuilder(
future: PackageInfo.fromPlatform(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
return Text( return Text(
'v${snapshot.data!.version} · ${snapshot.data!.buildNumber}', 'v${snapshot.data!.version} · ${snapshot.data!.buildNumber}',
style: const TextStyle(fontFamily: 'monospace'), style: const TextStyle(fontFamily: 'monospace'),
); );
}, },
), ),
Text('Copyright © ${DateTime.now().year} Solsynth LLC'), Text('Copyright © ${DateTime.now().year} Solsynth LLC'),
const Gap(16), const Gap(16),
Container( Container(
constraints: const BoxConstraints(maxWidth: 280), constraints: const BoxConstraints(maxWidth: 280),
child: Wrap( child: Wrap(
spacing: 4, spacing: 4,
runSpacing: 4, runSpacing: 4,
alignment: WrapAlignment.center, alignment: WrapAlignment.center,
children: [ children: [
TextButton( TextButton(
style: denseButtonStyle, style: denseButtonStyle,
child: Text('appDetails').tr(), child: Text('appDetails').tr(),
onPressed: () async { onPressed: () async {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
if (!context.mounted) return; if (!context.mounted) return;
showAboutDialog( showAboutDialog(
context: context, context: context,
applicationName: 'Solian', applicationName: 'Solian',
applicationVersion: '${info.version}+${info.buildNumber}', applicationVersion: '${info.version}+${info.buildNumber}',
applicationLegalese: applicationLegalese:
'The Solar Network App is an intuitive and open-source social network and computing platform. Experience the freedom of a user-friendly design that empowers you to create and connect with communities on your own terms. Embrace the future of social networking with a platform that prioritizes your independence and privacy.', 'The Solar Network App is an intuitive and open-source social network and computing platform. Experience the freedom of a user-friendly design that empowers you to create and connect with communities on your own terms. Embrace the future of social networking with a platform that prioritizes your independence and privacy.',
applicationIcon: ClipRRect( applicationIcon: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(16)), borderRadius: const BorderRadius.all(Radius.circular(16)),
child: Image.asset( child: Image.asset(
'assets/icon/icon-light-radius.png', 'assets/icon/icon-light-radius.png',
width: 60, width: 60,
height: 60, height: 60,
),
), ),
), );
); },
}, ),
), TextButton(
TextButton( style: denseButtonStyle,
style: denseButtonStyle, child: Text('termRelated').tr(),
child: Text('termRelated').tr(), onPressed: () {
onPressed: () { launchUrlString('https://solsynth.dev/terms');
launchUrlString('https://solsynth.dev/terms'); },
}, ),
), TextButton(
TextButton( style: denseButtonStyle,
style: denseButtonStyle, child: Text('serviceStatus').tr(),
child: Text('serviceStatus').tr(), onPressed: () {
onPressed: () { launchUrlString('https://status.solsynth.dev');
launchUrlString('https://status.solsynth.dev'); },
}, ),
), ],
], ),
).center(),
const Gap(16),
const Text(
'Open-sourced under AGPLv3',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 12,
),
), ),
).center(), ],
const Gap(16), ),
const Text(
'Open-sourced under AGPLv3',
style: TextStyle(
fontWeight: FontWeight.w300,
fontSize: 12,
),
),
],
), ),
); );
} }

View File

@ -95,36 +95,6 @@ class PageBackButton extends StatelessWidget {
} }
} }
class AppPageScaffold extends StatelessWidget {
final String? title;
final Widget? body;
final bool showAppBar;
const AppPageScaffold({
super.key,
this.title,
this.body,
this.showAppBar = true,
});
@override
Widget build(BuildContext context) {
final state = GoRouter.maybeOf(context);
final routeName = state?.routerDelegate.currentConfiguration.last.route.name;
final autoTitle = state != null ? 'screen${routeName?.capitalize()}' : 'screen';
return AppScaffold(
appBar: showAppBar
? AppBar(
title: Text(title ?? autoTitle.tr()),
)
: null,
body: body,
);
}
}
class AppRootScaffold extends StatelessWidget { class AppRootScaffold extends StatelessWidget {
final Widget body; final Widget body;