🎨 Optimized code structure, rename a lot of widgets

💄 Better large screen support in chat
This commit is contained in:
2024-05-03 13:39:52 +08:00
parent e080f49935
commit c0680a3134
48 changed files with 329 additions and 317 deletions

View File

@ -4,10 +4,10 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/account/friend.dart';
import 'package:solian/screens/account/personalize.dart';
import 'package:solian/widgets/account/avatar.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/widgets/empty.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
class AccountScreen extends StatefulWidget {
const AccountScreen({super.key});
@ -32,11 +32,11 @@ class _AccountScreenState extends State<AccountScreen> {
case 'account.personalize':
return const PersonalizeScreenWidget();
default:
return const SelectionEmptyWidget();
return const PageEmptyWidget();
}
}
return IndentWrapper(
return IndentScaffold(
title: _title ?? AppLocalizations.of(context)!.account,
noSafeArea: true,
fixedAppBarColor: true,
@ -60,7 +60,7 @@ class _AccountScreenState extends State<AccountScreen> {
)
: AccountScreenWidget(
onSelect: (item, _) {
router.pushNamed(item);
SolianRouter.router.pushNamed(item);
},
),
);
@ -139,7 +139,7 @@ class _AccountScreenWidgetState extends State<AccountScreenWidget> {
title: AppLocalizations.of(context)!.signIn,
caption: AppLocalizations.of(context)!.signInCaption,
onTap: () {
router.pushNamed('auth.sign-in').then((did) {
SolianRouter.router.pushNamed('auth.sign-in').then((did) {
auth.isAuthorized().then((value) {
setState(() => _isAuthorized = value);
});
@ -151,7 +151,7 @@ class _AccountScreenWidgetState extends State<AccountScreenWidget> {
title: AppLocalizations.of(context)!.signUp,
caption: AppLocalizations.of(context)!.signUpCaption,
onTap: () {
router.pushNamed('auth.sign-up');
SolianRouter.router.pushNamed('auth.sign-up');
},
),
],

View File

@ -6,9 +6,9 @@ import 'package:provider/provider.dart';
import 'package:solian/models/friendship.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/account/avatar.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class FriendScreen extends StatelessWidget {
@ -16,7 +16,7 @@ class FriendScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.friend,
noSafeArea: true,
hideDrawer: true,

View File

@ -7,7 +7,7 @@ import 'package:provider/provider.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class PersonalizeScreen extends StatelessWidget {
@ -15,7 +15,7 @@ class PersonalizeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.personalize,
hideDrawer: true,
child: const PersonalizeScreenWidget(),

View File

@ -5,7 +5,7 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:url_launcher/url_launcher_string.dart';
class SignInScreen extends StatelessWidget {
@ -21,7 +21,7 @@ class SignInScreen extends StatelessWidget {
final password = _passwordController.value.text;
if (username.isEmpty || password.isEmpty) return;
auth.signin(context, username, password).then((_) {
router.pop(true);
SolianRouter.router.pop(true);
}).catchError((e) {
List<String> messages = e.toString().split('\n');
if (messages.last.contains('risk')) {
@ -61,7 +61,7 @@ class SignInScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.signIn,
hideDrawer: true,
child: Center(

View File

@ -5,7 +5,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:http/http.dart' as http;
class SignUpScreen extends StatelessWidget {
@ -58,7 +58,7 @@ class SignUpScreen extends StatelessWidget {
);
},
).then((_) {
router.replaceNamed('auth.sign-in');
SolianRouter.router.replaceNamed('auth.sign-in');
});
} else {
var message = utf8.decode(res.bodyBytes);
@ -68,7 +68,7 @@ class SignUpScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.signUp,
hideDrawer: true,
child: Center(

View File

@ -3,10 +3,11 @@ import 'package:livekit_client/livekit_client.dart';
import 'package:provider/provider.dart';
import 'package:solian/models/call.dart';
import 'package:solian/providers/chat.dart';
import 'package:solian/widgets/chat/call/controls.dart';
import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/chat/call/call_controls.dart';
import 'package:solian/widgets/chat/call/participant.dart';
import 'package:solian/widgets/chat/call/participant_menu.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'dart:math' as math;
@ -127,8 +128,9 @@ class _ChatCallState extends State<ChatCall> {
);
}
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.chatCall,
fixedAppBarColor: SolianTheme.isLargeScreen(context),
hideDrawer: true,
child: content,
);

View File

@ -9,7 +9,7 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:uuid/uuid.dart';
@ -55,8 +55,8 @@ class _ChannelEditorScreenState extends State<ChannelEditorScreen> {
var message = utf8.decode(res.bodyBytes);
context.showErrorDialog(message);
} else {
if (router.canPop()) {
router.pop(true);
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop(true);
}
}
setState(() => _isSubmitting = false);
@ -67,8 +67,8 @@ class _ChannelEditorScreenState extends State<ChannelEditorScreen> {
}
void cancelEditing() {
if (router.canPop()) {
router.pop(false);
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop(false);
}
}
@ -99,7 +99,7 @@ class _ChannelEditorScreenState extends State<ChannelEditorScreen> {
],
);
return IndentWrapper(
return IndentScaffold(
hideDrawer: true,
title: AppLocalizations.of(context)!.chatChannelOrganize,
appBarActions: <Widget>[

View File

@ -7,10 +7,10 @@ import 'package:solian/models/account.dart';
import 'package:solian/models/channel.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/account/avatar.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/account/friend_picker.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class ChatMemberScreen extends StatefulWidget {
@ -141,7 +141,7 @@ class _ChatMemberScreenState extends State<ChatMemberScreen> {
@override
Widget build(BuildContext context) {
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.chatMember,
noSafeArea: true,
hideDrawer: true,

View File

@ -10,12 +10,13 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/providers/chat.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/chat/channel_action.dart';
import 'package:solian/widgets/chat/maintainer.dart';
import 'package:solian/widgets/chat/chat_maintainer.dart';
import 'package:solian/widgets/chat/message.dart';
import 'package:solian/widgets/chat/message_action.dart';
import 'package:solian/widgets/chat/message_editor.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class ChatScreen extends StatelessWidget {
@ -27,9 +28,11 @@ class ChatScreen extends StatelessWidget {
Widget build(BuildContext context) {
final chat = context.watch<ChatProvider>();
return IndentWrapper(
return IndentScaffold(
title: chat.focusChannel?.name ?? 'Loading...',
hideDrawer: true,
fixedAppBarColor: SolianTheme.isLargeScreen(context),
appBarLeading: IconButton(icon: const Icon(Icons.tag), onPressed: () {}),
appBarActions: chat.focusChannel != null
? [
ChannelCallAction(
@ -197,7 +200,7 @@ class _ChatScreenWidgetState extends State<ChatScreenWidget> {
TextButton(
child: Text(AppLocalizations.of(context)!.chatCallJoin),
onPressed: () {
router.pushNamed(
SolianRouter.router.pushNamed(
'chat.channel.call',
extra: _chat.ongoingCall,
pathParameters: {'channel': widget.alias},

View File

@ -4,19 +4,19 @@ import 'package:solian/models/channel.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/widgets/chat/channel_deletion.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class ChatManageScreen extends StatefulWidget {
class ChatDetailScreen extends StatefulWidget {
final Channel channel;
const ChatManageScreen({super.key, required this.channel});
const ChatDetailScreen({super.key, required this.channel});
@override
State<ChatManageScreen> createState() => _ChatManageScreenState();
State<ChatDetailScreen> createState() => _ChatDetailScreenState();
}
class _ChatManageScreenState extends State<ChatManageScreen> {
class _ChatDetailScreenState extends State<ChatDetailScreen> {
bool _isOwned = false;
void promptLeaveChannel() async {
@ -27,8 +27,8 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
isOwned: _isOwned,
),
);
if (did == true && router.canPop()) {
router.pop('disposed');
if (did == true && SolianRouter.router.canPop()) {
SolianRouter.router.pop('disposed');
}
}
@ -53,16 +53,16 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
leading: const Icon(Icons.settings),
title: Text(AppLocalizations.of(context)!.settings),
onTap: () async {
router.pushNamed('chat.channel.editor', extra: widget.channel).then((did) {
SolianRouter.router.pushNamed('chat.channel.editor', extra: widget.channel).then((did) {
if (did == true) {
if (router.canPop()) router.pop('refresh');
if (SolianRouter.router.canPop()) SolianRouter.router.pop('refresh');
}
});
},
),
];
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.chatManage,
hideDrawer: true,
noSafeArea: true,
@ -100,7 +100,7 @@ class _ChatManageScreenState extends State<ChatManageScreen> {
leading: const Icon(Icons.supervisor_account),
title: Text(AppLocalizations.of(context)!.chatMember),
onTap: () {
router.pushNamed(
SolianRouter.router.pushNamed(
'chat.channel.member',
extra: widget.channel,
pathParameters: {'channel': widget.channel.alias},

View File

@ -4,90 +4,47 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:solian/models/channel.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/providers/chat.dart';
import 'package:solian/router.dart';
import 'package:solian/screens/chat/chat.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/chat/channel_action.dart';
import 'package:solian/utils/theme.dart';
import 'package:solian/widgets/chat/chat_new.dart';
import 'package:solian/widgets/empty.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/widgets/notification_notifier.dart';
import 'package:solian/widgets/signin_required.dart';
class ChatIndexScreen extends StatefulWidget {
const ChatIndexScreen({super.key});
@override
State<ChatIndexScreen> createState() => _ChatIndexScreenState();
}
class _ChatIndexScreenState extends State<ChatIndexScreen> {
Channel? _selectedChannel;
class ChatListScreen extends StatelessWidget {
const ChatListScreen({super.key});
@override
Widget build(BuildContext context) {
final chat = context.watch<ChatProvider>();
final screenWidth = MediaQuery.of(context).size.width;
final isLargeScreen = screenWidth >= 600;
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.chat,
appBarActions: isLargeScreen && chat.focusChannel != null
? [
ChannelCallAction(
call: chat.ongoingCall,
channel: chat.focusChannel!,
onUpdate: () => chat.fetchChannel(chat.focusChannel!.alias),
),
ChannelManageAction(
channel: chat.focusChannel!,
onUpdate: () => chat.fetchChannel(chat.focusChannel!.alias),
),
]
: [const NotificationButton()],
fixedAppBarColor: isLargeScreen,
child: isLargeScreen
? Row(
children: [
Flexible(
flex: 2,
child: ChatIndexScreenWidget(
onSelect: (item) {
setState(() => _selectedChannel = item);
},
),
),
const VerticalDivider(thickness: 0.3, width: 0.3),
Flexible(
flex: 4,
child: _selectedChannel == null
? const SelectionEmptyWidget()
: ChatScreenWidget(
key: Key('c${_selectedChannel!.id}'),
alias: _selectedChannel!.alias,
),
),
],
)
: const ChatIndexScreenWidget(),
appBarActions: const [NotificationButton()],
fixedAppBarColor: SolianTheme.isLargeScreen(context),
child: ChatListWidget(
onSelect: (item) {
SolianRouter.router.pushReplacementNamed(
'chat.channel',
pathParameters: {'channel': item.alias},
);
},
),
);
}
}
class ChatIndexScreenWidget extends StatefulWidget {
class ChatListWidget extends StatefulWidget {
final Function(Channel item)? onSelect;
const ChatIndexScreenWidget({super.key, this.onSelect});
const ChatListWidget({super.key, this.onSelect});
@override
State<ChatIndexScreenWidget> createState() => _ChatIndexScreenWidgetState();
State<ChatListWidget> createState() => _ChatListWidgetState();
}
class _ChatIndexScreenWidgetState extends State<ChatIndexScreenWidget> {
class _ChatListWidgetState extends State<ChatListWidget> {
List<Channel> _channels = List.empty();
Future<void> fetchChannels() async {
@ -169,7 +126,7 @@ class _ChatIndexScreenWidgetState extends State<ChatIndexScreenWidget> {
return;
}
final result = await router.pushNamed(
final result = await SolianRouter.router.pushNamed(
'chat.channel',
pathParameters: {
'channel': element.alias,

View File

@ -12,9 +12,9 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:http/http.dart' as http;
import 'package:solian/widgets/empty.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:solian/widgets/notification_notifier.dart';
import 'package:solian/widgets/posts/item.dart';
import 'package:solian/widgets/posts/post.dart';
class ExploreScreen extends StatefulWidget {
const ExploreScreen({super.key});
@ -31,7 +31,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
final screenWidth = MediaQuery.of(context).size.width;
final isLargeScreen = screenWidth >= 600;
return IndentWrapper(
return IndentScaffold(
noSafeArea: true,
fixedAppBarColor: isLargeScreen,
appBarActions: const [NotificationButton()],
@ -51,7 +51,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
Flexible(
flex: 4,
child: _selectedPost == null
? const SelectionEmptyWidget()
? const PageEmptyWidget()
: PostScreenWidget(
key: Key('p${_selectedPost!.id}'),
dataset: _selectedPost!.dataset,
@ -62,7 +62,7 @@ class _ExploreScreenState extends State<ExploreScreen> {
)
: ExploreScreenWidget(
onSelect: (item) {
router.pushNamed(
SolianRouter.router.pushNamed(
'posts.screen',
pathParameters: {
'alias': item.alias,
@ -130,7 +130,7 @@ class _ExploreScreenWidgetState extends State<ExploreScreenWidget> {
return FloatingActionButton(
child: const Icon(Icons.edit),
onPressed: () async {
final did = await router.pushNamed('posts.moments.editor');
final did = await SolianRouter.router.pushNamed('posts.moments.editor');
if (did == true) _pagingController.refresh();
},
);

View File

@ -3,7 +3,7 @@ import 'package:provider/provider.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/providers/notify.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:solian/models/notification.dart' as model;
@ -25,7 +25,7 @@ class _NotificationScreenState extends State<NotificationScreen> {
nty.allRead();
});
return IndentWrapper(
return IndentScaffold(
noSafeArea: true,
hideDrawer: true,
title: AppLocalizations.of(context)!.notification,
@ -82,10 +82,10 @@ class NotificationItem extends StatelessWidget {
const NotificationItem(
{super.key, required this.index, required this.item, this.onDismiss});
bool hasLinks() => item.links != null && item.links!.isNotEmpty;
bool get hasLinks => item.links != null && item.links!.isNotEmpty;
void showLinks(BuildContext context) {
if (!hasLinks()) return;
if (!hasLinks) return;
showModalBottomSheet<void>(
context: context,
@ -170,7 +170,7 @@ class NotificationItem extends StatelessWidget {
child: ListTile(
title: Text(item.subject),
subtitle: Text(item.content),
trailing: hasLinks()
trailing: hasLinks
? TextButton(
onPressed: () => showLinks(context),
style: TextButton.styleFrom(shape: const CircleBorder()),

View File

@ -9,9 +9,9 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/widgets/account/avatar.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:solian/widgets/posts/attachment_editor.dart';
class CommentPostArguments {
@ -78,16 +78,16 @@ class _CommentEditorScreenState extends State<CommentEditorScreen> {
var message = utf8.decode(res.bodyBytes);
context.showErrorDialog(message);
} else {
if (router.canPop()) {
router.pop(true);
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop(true);
}
}
setState(() => _isSubmitting = false);
}
void cancelEditing() {
if (router.canPop()) {
router.pop(false);
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop(false);
}
}
@ -120,7 +120,7 @@ class _CommentEditorScreenState extends State<CommentEditorScreen> {
],
);
return IndentWrapper(
return IndentScaffold(
hideDrawer: true,
title: AppLocalizations.of(context)!.newComment,
appBarActions: <Widget>[

View File

@ -9,9 +9,9 @@ import 'package:solian/providers/auth.dart';
import 'package:solian/router.dart';
import 'package:solian/utils/service_url.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:solian/widgets/account/avatar.dart';
import 'package:solian/widgets/account/account_avatar.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:solian/widgets/posts/attachment_editor.dart';
class MomentEditorScreen extends StatefulWidget {
@ -68,16 +68,16 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
var message = utf8.decode(res.bodyBytes);
context.showErrorDialog(message);
} else {
if (router.canPop()) {
router.pop(true);
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop(true);
}
}
setState(() => _isSubmitting = false);
}
void cancelEditing() {
if (router.canPop()) {
router.pop(false);
if (SolianRouter.router.canPop()) {
SolianRouter.router.pop(false);
}
}
@ -110,7 +110,7 @@ class _MomentEditorScreenState extends State<MomentEditorScreen> {
],
);
return IndentWrapper(
return IndentScaffold(
hideDrawer: true,
title: AppLocalizations.of(context)!.newMoment,
appBarActions: <Widget>[

View File

@ -6,9 +6,9 @@ import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import 'package:solian/models/post.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/exts.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:solian/widgets/scaffold.dart';
import 'package:solian/widgets/posts/comment_list.dart';
import 'package:solian/widgets/posts/item.dart';
import 'package:solian/widgets/posts/post.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
class PostScreen extends StatelessWidget {
@ -19,7 +19,7 @@ class PostScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return IndentWrapper(
return IndentScaffold(
title: AppLocalizations.of(context)!.post,
noSafeArea: true,
hideDrawer: true,