diff --git a/lib/router.dart b/lib/router.dart index 40aab6b..52f8257 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -282,7 +282,7 @@ final _appRoutes = [ ), ), ShellRoute( - builder: (context, state, child) => AppPageScaffold(body: child), + builder: (context, state, child) => child, routes: [ GoRoute( path: '/settings', @@ -292,7 +292,7 @@ final _appRoutes = [ ], ), ShellRoute( - builder: (context, state, child) => AppPageScaffold(body: child), + builder: (context, state, child) => child, routes: [ GoRoute( path: '/about', diff --git a/lib/screens/account/profile_edit.dart b/lib/screens/account/profile_edit.dart index cfa98a5..df1a477 100644 --- a/lib/screens/account/profile_edit.dart +++ b/lib/screens/account/profile_edit.dart @@ -210,7 +210,7 @@ class _ProfileEditScreenState extends State { return AppScaffold( appBar: AppBar( leading: const PageBackButton(), - title: Text('screenProfileEdit').tr(), + title: Text('screenAccountProfileEdit').tr(), ), body: SingleChildScrollView( child: Column( diff --git a/lib/screens/friend.dart b/lib/screens/friend.dart index e15e349..3447dce 100644 --- a/lib/screens/friend.dart +++ b/lib/screens/friend.dart @@ -234,52 +234,56 @@ class _FriendScreenState extends State { if (_requests.isNotEmpty || _blocks.isNotEmpty) const Divider(height: 1), Expanded( - child: RefreshIndicator( - onRefresh: () => Future.wait([ - _fetchRelations(), - _fetchRequests(), - ]), - child: ListView.builder( - itemCount: _relations.length, - itemBuilder: (context, index) { - final relation = _relations[index]; - final other = relation.related; - return ListTile( - contentPadding: const EdgeInsets.only(right: 24, left: 16), - leading: AccountImage(content: other?.avatar), - title: Text(other?.nick ?? 'unknown'), - subtitle: Text(other?.nick ?? 'unknown'), - trailing: SizedBox( - height: 48, - width: 120, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - InkWell( - onTap: _isUpdating - ? null - : () => _changeRelation(relation, 2), - child: Text('friendBlock').tr(), - ), - const Gap(8), - InkWell( - onTap: _isUpdating - ? null - : () => _deleteRelation(relation), - child: Text('friendDeleteAction').tr(), - ), - ], - ), - ], + child: MediaQuery.removePadding( + context: context, + removeTop: true, + child: RefreshIndicator( + onRefresh: () => Future.wait([ + _fetchRelations(), + _fetchRequests(), + ]), + child: ListView.builder( + itemCount: _relations.length, + itemBuilder: (context, index) { + final relation = _relations[index]; + final other = relation.related; + return ListTile( + contentPadding: const EdgeInsets.only(right: 24, left: 16), + leading: AccountImage(content: other?.avatar), + title: Text(other?.nick ?? 'unknown'), + subtitle: Text(other?.nick ?? 'unknown'), + trailing: SizedBox( + height: 48, + width: 120, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + InkWell( + onTap: _isUpdating + ? null + : () => _changeRelation(relation, 2), + child: Text('friendBlock').tr(), + ), + const Gap(8), + InkWell( + onTap: _isUpdating + ? null + : () => _deleteRelation(relation), + child: Text('friendDeleteAction').tr(), + ), + ], + ), + ], + ), ), - ), - ); - }, + ); + }, + ), ), ), ), diff --git a/lib/screens/realm.dart b/lib/screens/realm.dart index 0a309bf..7346547 100644 --- a/lib/screens/realm.dart +++ b/lib/screens/realm.dart @@ -119,113 +119,61 @@ class _RealmScreenState extends State { children: [ LoadingIndicator(isActive: _isBusy), Expanded( - child: RefreshIndicator( - onRefresh: _fetchRealms, - child: ListView.builder( - itemCount: _realms?.length ?? 0, - itemBuilder: (context, idx) { - final realm = _realms![idx]; - if (_isCompactView) { - return ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - leading: AccountImage( - content: realm.avatar, - fallbackWidget: const Icon(Symbols.group, size: 20), - ), - title: Text(realm.name), - subtitle: Text( - realm.description, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - trailing: PopupMenuButton( - itemBuilder: (BuildContext context) => [ - PopupMenuItem( - child: Row( - children: [ - const Icon(Symbols.edit), - const Gap(16), - 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, + child: MediaQuery.removePadding( + context: context, + removeTop: true, + child: RefreshIndicator( + onRefresh: _fetchRealms, + child: ListView.builder( + itemCount: _realms?.length ?? 0, + itemBuilder: (context, idx) { + final realm = _realms![idx]; + if (_isCompactView) { + return ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + leading: AccountImage( + content: realm.avatar, + fallbackWidget: const Icon(Symbols.group, size: 20), + ), + title: Text(realm.name), + subtitle: Text( + realm.description, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + trailing: PopupMenuButton( + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + child: Row( children: [ - 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 Icon(Symbols.edit), + const Gap(16), + 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); + }, ), - 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: () { @@ -234,10 +182,69 @@ class _RealmScreenState extends State { 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(); + }, + ), ), ), ), diff --git a/lib/screens/realm/realm_detail.dart b/lib/screens/realm/realm_detail.dart index d700655..92e699d 100644 --- a/lib/screens/realm/realm_detail.dart +++ b/lib/screens/realm/realm_detail.dart @@ -8,14 +8,13 @@ import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/sn_network.dart'; import 'package:surface/providers/user_directory.dart'; import 'package:surface/providers/userinfo.dart'; +import 'package:surface/types/post.dart'; import 'package:surface/types/realm.dart'; import 'package:surface/widgets/account/account_image.dart'; import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:very_good_infinite_list/very_good_infinite_list.dart'; -import '../../types/post.dart'; - class RealmDetailScreen extends StatefulWidget { final String alias; @@ -421,7 +420,7 @@ class _RealmSettingsWidgetState extends State<_RealmSettingsWidget> { return Column( children: [ - const Gap(16), + const Gap(8), ListTile( leading: const Icon(Symbols.edit), trailing: const Icon(Symbols.chevron_right), diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index 6e34752..064e3ee 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -68,8 +68,11 @@ class _SettingsScreenState extends State { Widget build(BuildContext context) { final sn = context.read(); - return Scaffold( - backgroundColor: Colors.transparent, + return AppScaffold( + appBar: AppBar( + leading: const PageBackButton(), + title: Text('screenSettings').tr(), + ), body: SingleChildScrollView( child: Column( spacing: 16, diff --git a/lib/widgets/about.dart b/lib/widgets/about.dart index f5ea629..1017092 100644 --- a/lib/widgets/about.dart +++ b/lib/widgets/about.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:gap/gap.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:url_launcher/url_launcher_string.dart'; class AboutScreen extends StatelessWidget { @@ -12,97 +13,103 @@ class AboutScreen extends StatelessWidget { Widget build(BuildContext context) { const denseButtonStyle = ButtonStyle(visualDensity: VisualDensity(vertical: -4)); - return SizedBox( - width: double.infinity, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16)), - child: Image.asset('assets/icon/icon-light-radius.png', width: 120, height: 120), - ), - const Gap(8), - Text( - 'Solian', - style: Theme.of(context).textTheme.titleLarge!.copyWith(fontSize: 36), - ), - const Text( - 'The Solar Network', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - ), - const Gap(8), - FutureBuilder( - future: PackageInfo.fromPlatform(), - builder: (context, snapshot) { - if (!snapshot.hasData) { - return const SizedBox.shrink(); - } + return AppScaffold( + appBar: AppBar( + leading: const PageBackButton(), + title: Text('screenAbout').tr(), + ), + body: SizedBox( + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: Image.asset('assets/icon/icon-light-radius.png', width: 120, height: 120), + ), + const Gap(8), + Text( + 'Solian', + style: Theme.of(context).textTheme.titleLarge!.copyWith(fontSize: 36), + ), + const Text( + 'The Solar Network', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const Gap(8), + FutureBuilder( + future: PackageInfo.fromPlatform(), + builder: (context, snapshot) { + if (!snapshot.hasData) { + return const SizedBox.shrink(); + } - return Text( - 'v${snapshot.data!.version} · ${snapshot.data!.buildNumber}', - style: const TextStyle(fontFamily: 'monospace'), - ); - }, - ), - Text('Copyright © ${DateTime.now().year} Solsynth LLC'), - const Gap(16), - Container( - constraints: const BoxConstraints(maxWidth: 280), - child: Wrap( - spacing: 4, - runSpacing: 4, - alignment: WrapAlignment.center, - children: [ - TextButton( - style: denseButtonStyle, - child: Text('appDetails').tr(), - onPressed: () async { - final info = await PackageInfo.fromPlatform(); + return Text( + 'v${snapshot.data!.version} · ${snapshot.data!.buildNumber}', + style: const TextStyle(fontFamily: 'monospace'), + ); + }, + ), + Text('Copyright © ${DateTime.now().year} Solsynth LLC'), + const Gap(16), + Container( + constraints: const BoxConstraints(maxWidth: 280), + child: Wrap( + spacing: 4, + runSpacing: 4, + alignment: WrapAlignment.center, + children: [ + TextButton( + style: denseButtonStyle, + child: Text('appDetails').tr(), + onPressed: () async { + final info = await PackageInfo.fromPlatform(); - if (!context.mounted) return; - showAboutDialog( - context: context, - applicationName: 'Solian', - applicationVersion: '${info.version}+${info.buildNumber}', - 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.', - applicationIcon: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(16)), - child: Image.asset( - 'assets/icon/icon-light-radius.png', - width: 60, - height: 60, + if (!context.mounted) return; + showAboutDialog( + context: context, + applicationName: 'Solian', + applicationVersion: '${info.version}+${info.buildNumber}', + 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.', + applicationIcon: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(16)), + child: Image.asset( + 'assets/icon/icon-light-radius.png', + width: 60, + height: 60, + ), ), - ), - ); - }, - ), - TextButton( - style: denseButtonStyle, - child: Text('termRelated').tr(), - onPressed: () { - launchUrlString('https://solsynth.dev/terms'); - }, - ), - TextButton( - style: denseButtonStyle, - child: Text('serviceStatus').tr(), - onPressed: () { - launchUrlString('https://status.solsynth.dev'); - }, - ), - ], + ); + }, + ), + TextButton( + style: denseButtonStyle, + child: Text('termRelated').tr(), + onPressed: () { + launchUrlString('https://solsynth.dev/terms'); + }, + ), + TextButton( + style: denseButtonStyle, + child: Text('serviceStatus').tr(), + onPressed: () { + 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, - ), - ), - ], + ], + ), ), ); } diff --git a/lib/widgets/navigation/app_scaffold.dart b/lib/widgets/navigation/app_scaffold.dart index c2a2cdf..d52ee0a 100644 --- a/lib/widgets/navigation/app_scaffold.dart +++ b/lib/widgets/navigation/app_scaffold.dart @@ -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 { final Widget body;