sliver refactor

This commit is contained in:
Christien Rioux 2024-07-11 23:04:08 -04:00
parent 67812b3c6f
commit 2fa3cbd21c
25 changed files with 710 additions and 387 deletions

View File

@ -97,8 +97,9 @@
"invalid_account_title": "Invalid Account", "invalid_account_title": "Invalid Account",
"invalid_account_text": "Account is invalid, removing from list" "invalid_account_text": "Account is invalid, removing from list"
}, },
"account_page": { "contacts_page": {
"contact_invitations": "Contact Invitations" "contacts": "Contacts",
"invitations": "Invitations"
}, },
"add_contact_sheet": { "add_contact_sheet": {
"new_contact": "New Contact", "new_contact": "New Contact",
@ -176,14 +177,13 @@
"password_does_not_match": "Password does not match" "password_does_not_match": "Password does not match"
}, },
"contact_list": { "contact_list": {
"title": "Contacts",
"invite_people": "Invite people to VeilidChat", "invite_people": "Invite people to VeilidChat",
"search": "Search contacts", "search": "Search contacts",
"invitation": "Invitation" "invitation": "Invitation"
}, },
"chat_list": { "chat_list": {
"search": "Search chats", "search": "Search chats",
"start_a_conversation": "Start a conversation", "start_a_conversation": "Start A Conversation",
"chats": "Chats", "chats": "Chats",
"groups": "Groups" "groups": "Groups"
}, },

View File

@ -58,6 +58,8 @@ PODS:
- nanopb/encode (= 2.30910.0) - nanopb/encode (= 2.30910.0)
- nanopb/decode (2.30910.0) - nanopb/decode (2.30910.0)
- nanopb/encode (2.30910.0) - nanopb/encode (2.30910.0)
- native_device_orientation (0.0.1):
- Flutter
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
- pasteboard (0.0.1): - pasteboard (0.0.1):
@ -91,6 +93,7 @@ DEPENDENCIES:
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
- native_device_orientation (from `.symlinks/plugins/native_device_orientation/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- pasteboard (from `.symlinks/plugins/pasteboard/ios`) - pasteboard (from `.symlinks/plugins/pasteboard/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
@ -129,6 +132,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_native_splash/ios" :path: ".symlinks/plugins/flutter_native_splash/ios"
mobile_scanner: mobile_scanner:
:path: ".symlinks/plugins/mobile_scanner/ios" :path: ".symlinks/plugins/mobile_scanner/ios"
native_device_orientation:
:path: ".symlinks/plugins/native_device_orientation/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
pasteboard: pasteboard:
@ -169,6 +174,7 @@ SPEC CHECKSUMS:
MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1 MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1
mobile_scanner: 8564358885a9253c43f822435b70f9345c87224f mobile_scanner: 8564358885a9253c43f822435b70f9345c87224f
nanopb: 438bc412db1928dac798aa6fd75726007be04262 nanopb: 438bc412db1928dac798aa6fd75726007be04262
native_device_orientation: 348b10c346a60ebbc62fb235a4fdb5d1b61a8f55
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0 pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46

View File

@ -44,25 +44,11 @@ class EditAccountPage extends StatefulWidget {
} }
} }
class _EditAccountPageState extends State<EditAccountPage> { class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
bool _isInAsyncCall = false; _EditAccountPageState()
: super(
@override titleBarStyle: TitleBarStyle.normal,
void initState() { orientationCapability: OrientationCapability.portraitOnly);
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.portraitOnly);
});
}
@override
void dispose() {
unawaited(
changeWindowSetup(TitleBarStyle.normal, OrientationCapability.normal));
super.dispose();
}
Widget _editAccountForm(BuildContext context, Widget _editAccountForm(BuildContext context,
{required Future<void> Function(GlobalKey<FormBuilderState>) {required Future<void> Function(GlobalKey<FormBuilderState>)
@ -314,4 +300,8 @@ class _EditAccountPageState extends State<EditAccountPage> {
]).paddingSymmetric(horizontal: 24, vertical: 8))) ]).paddingSymmetric(horizontal: 24, vertical: 8)))
.withModalHUD(context, displayModalHUD); .withModalHUD(context, displayModalHUD);
} }
////////////////////////////////////////////////////////////////////////////
bool _isInAsyncCall = false;
} }

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -20,18 +22,11 @@ class NewAccountPage extends StatefulWidget {
State createState() => _NewAccountPageState(); State createState() => _NewAccountPageState();
} }
class _NewAccountPageState extends State<NewAccountPage> { class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
bool _isInAsyncCall = false; _NewAccountPageState()
: super(
@override titleBarStyle: TitleBarStyle.normal,
void initState() { orientationCapability: OrientationCapability.portraitOnly);
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.portraitOnly);
});
}
Widget _newAccountForm(BuildContext context, Widget _newAccountForm(BuildContext context,
{required Future<void> Function(GlobalKey<FormBuilderState>) onSubmit}) { {required Future<void> Function(GlobalKey<FormBuilderState>) onSubmit}) {
@ -120,4 +115,8 @@ class _NewAccountPageState extends State<NewAccountPage> {
)).paddingSymmetric(horizontal: 24, vertical: 8), )).paddingSymmetric(horizontal: 24, vertical: 8),
).withModalHUD(context, displayModalHUD); ).withModalHUD(context, displayModalHUD);
} }
////////////////////////////////////////////////////////////////////////////
bool _isInAsyncCall = false;
} }

View File

@ -43,7 +43,7 @@ class ProfileWidget extends StatelessWidget {
: scale.primaryScale.borderText, : scale.primaryScale.borderText,
width: 2), width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))), Radius.circular(12 * scaleConfig.borderRadiusScale))),
), ),
child: Row(children: [ child: Row(children: [
const Spacer(), const Spacer(),

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
@ -29,22 +30,17 @@ class ShowRecoveryKeyPage extends StatefulWidget {
_name = name; _name = name;
@override @override
ShowRecoveryKeyPageState createState() => ShowRecoveryKeyPageState(); State<ShowRecoveryKeyPage> createState() => _ShowRecoveryKeyPageState();
final WritableSuperIdentity _writableSuperIdentity; final WritableSuperIdentity _writableSuperIdentity;
final String _name; final String _name;
} }
class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> { class _ShowRecoveryKeyPageState extends WindowSetupState<ShowRecoveryKeyPage> {
@override _ShowRecoveryKeyPageState()
void initState() { : super(
super.initState(); titleBarStyle: TitleBarStyle.normal,
orientationCapability: OrientationCapability.portraitOnly);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.portraitOnly);
});
}
Future<void> _shareRecoveryKey( Future<void> _shareRecoveryKey(
BuildContext context, Uint8List recoveryKey, String name) async { BuildContext context, Uint8List recoveryKey, String name) async {

View File

@ -15,7 +15,6 @@ import 'init.dart';
import 'layout/splash.dart'; import 'layout/splash.dart';
import 'router/router.dart'; import 'router/router.dart';
import 'settings/settings.dart'; import 'settings/settings.dart';
import 'theme/models/theme_preference.dart';
import 'theme/theme.dart'; import 'theme/theme.dart';
import 'tick.dart'; import 'tick.dart';
import 'tools/loggy.dart'; import 'tools/loggy.dart';
@ -92,7 +91,7 @@ class VeilidChatApp extends StatelessWidget {
Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>( Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>(
initialData: null, initialData: null,
create: (context) async => VeilidChatGlobalInit.initialize(), create: (context) async => VeilidChatGlobalInit.initialize(),
builder: (context, child) { builder: (context, __) {
final globalInit = context.watch<VeilidChatGlobalInit?>(); final globalInit = context.watch<VeilidChatGlobalInit?>();
if (globalInit == null) { if (globalInit == null) {
// Splash screen until we're done with init // Splash screen until we're done with init

View File

@ -60,36 +60,33 @@ class ChatListWidget extends StatelessWidget {
final chatListV = context.watch<ChatListCubit>().state; final chatListV = context.watch<ChatListCubit>().state;
return chatListV return chatListV
.builder((context, chatList) => SizedBox.expand( .builder((context, chatList) => SizedBox.expand(
child: styledTitleContainer( child: styledTitleContainer(
context: context, context: context,
title: translate('chat_list.chats'), title: translate('chat_list.chats'),
child: SizedBox.expand( child: (chatList.isEmpty)
child: (chatList.isEmpty) ? const SizedBox.expand(child: EmptyChatListWidget())
? const EmptyChatListWidget() : SearchableList<proto.Chat>(
: SearchableList<proto.Chat>( initialList: chatList.map((x) => x.value).toList(),
initialList: chatList.map((x) => x.value).toList(), itemBuilder: (c) {
itemBuilder: (c) { switch (c.whichKind()) {
switch (c.whichKind()) { case proto.Chat_Kind.direct:
case proto.Chat_Kind.direct: return _itemBuilderDirect(c.direct, contactMap,
return _itemBuilderDirect( contactListV.busy || chatListV.busy);
c.direct, case proto.Chat_Kind.group:
contactMap, return const Text(
contactListV.busy || chatListV.busy); 'group chats not yet supported!');
case proto.Chat_Kind.group: case proto.Chat_Kind.notSet:
return const Text( throw StateError('unknown chat kind');
'group chats not yet supported!'); }
case proto.Chat_Kind.notSet: },
throw StateError('unknown chat kind'); filter: (value) =>
} _itemFilter(contactMap, chatList, value),
}, spaceBetweenSearchAndList: 4,
filter: (value) => inputDecoration: InputDecoration(
_itemFilter(contactMap, chatList, value), labelText: translate('chat_list.search'),
spaceBetweenSearchAndList: 4, ),
inputDecoration: InputDecoration( ).paddingAll(8),
labelText: translate('chat_list.search'), )))
),
),
).paddingAll(8))))
.paddingLTRB(8, 0, 8, 8); .paddingLTRB(8, 0, 8, 8);
}); });
} }

View File

@ -2,6 +2,7 @@ import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../proto/proto.dart' as proto; import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart'; import '../../theme/theme.dart';
@ -31,58 +32,58 @@ class ContactInvitationListWidget extends StatefulWidget {
} }
class ContactInvitationListWidgetState class ContactInvitationListWidgetState
extends State<ContactInvitationListWidget> { extends State<ContactInvitationListWidget>
final ScrollController _scrollController = ScrollController(); with SingleTickerProviderStateMixin {
late final _controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 250), value: 1);
late final _animation =
CurvedAnimation(parent: _controller, curve: Curves.easeInOut);
bool _expanded = true;
@override @override
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
//final textTheme = theme.textTheme; // final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!; final scaleConfig = theme.extension<ScaleConfig>()!;
return Container( return styledExpandingSliver(
width: double.infinity, context: context,
margin: const EdgeInsets.fromLTRB(4, 0, 4, 4), animation: _animation,
decoration: ShapeDecoration( expanded: _expanded,
shape: RoundedRectangleBorder( backgroundColor: scaleConfig.preferBorders
borderRadius: BorderRadius.circular(16 * scaleConfig.borderRadiusScale), ? scale.primaryScale.subtleBackground
)), : scale.primaryScale.subtleBorder,
constraints: const BoxConstraints(maxHeight: 100), onTap: () {
child: Container( setState(() {
width: double.infinity, _expanded = !_expanded;
decoration: ShapeDecoration( });
color: scale.primaryScale.subtleBackground, _controller.animateTo(_expanded ? 1 : 0);
shape: RoundedRectangleBorder( },
borderRadius: title: translate('contacts_page.invitations'),
BorderRadius.circular(16 * scaleConfig.borderRadiusScale), sliver: SliverList.builder(
)), itemCount: widget.contactInvitationRecordList.length,
child: ListView.builder( itemBuilder: (context, index) {
shrinkWrap: true, if (index < 0 ||
controller: _scrollController, index >= widget.contactInvitationRecordList.length) {
itemCount: widget.contactInvitationRecordList.length, return null;
itemBuilder: (context, index) { }
if (index < 0 || return ContactInvitationItemWidget(
index >= widget.contactInvitationRecordList.length) { contactInvitationRecord:
return null; widget.contactInvitationRecordList[index],
} disabled: widget.disabled,
return ContactInvitationItemWidget( key: ObjectKey(widget.contactInvitationRecordList[index]))
contactInvitationRecord: .paddingLTRB(4, 2, 4, 2);
widget.contactInvitationRecordList[index], },
disabled: widget.disabled, findChildIndexCallback: (key) {
key: ObjectKey(widget.contactInvitationRecordList[index])) final index = widget.contactInvitationRecordList.indexOf(
.paddingLTRB(4, 2, 4, 2); (key as ObjectKey).value! as proto.ContactInvitationRecord);
}, if (index == -1) {
findChildIndexCallback: (key) { return null;
final index = widget.contactInvitationRecordList.indexOf( }
(key as ObjectKey).value! as proto.ContactInvitationRecord); return index;
if (index == -1) { },
return null; ));
}
return index;
},
).paddingLTRB(4, 6, 4, 6)),
);
} }
} }

View File

@ -28,49 +28,50 @@ class ContactListWidget extends StatefulWidget {
} }
} }
class _ContactListWidgetState extends State<ContactListWidget> { class _ContactListWidgetState extends State<ContactListWidget>
@override with SingleTickerProviderStateMixin {
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
return styledTitleContainer( return SliverLayoutBuilder(
context: context, builder: (context, constraints) => styledHeaderSliver(
title: translate('contact_list.title'), context: context,
child: SearchableList<proto.Contact>( backgroundColor: scaleConfig.preferBorders
shrinkWrap: true, ? scale.primaryScale.subtleBackground
initialList: widget.contactList.toList(), : scale.primaryScale.subtleBorder,
itemBuilder: (c) => title: translate('contacts_page.contacts'),
ContactItemWidget(contact: c, disabled: widget.disabled) sliver: SliverFillRemaining(
.paddingLTRB(0, 4, 0, 0), child: SearchableList<proto.Contact>.sliver(
filter: (value) { initialList: widget.contactList.toList(),
final lowerValue = value.toLowerCase(); itemBuilder: (c) =>
return widget.contactList ContactItemWidget(contact: c, disabled: widget.disabled)
.where((element) => .paddingLTRB(0, 4, 0, 0),
element.nickname.toLowerCase().contains(lowerValue) || filter: (value) {
element.profile.name.toLowerCase().contains(lowerValue) || final lowerValue = value.toLowerCase();
element.profile.pronouns.toLowerCase().contains(lowerValue)) return widget.contactList
.toList(); .where((element) =>
}, element.nickname.toLowerCase().contains(lowerValue) ||
searchFieldHeight: 40, element.profile.name
spaceBetweenSearchAndList: 4, .toLowerCase()
emptyWidget: const EmptyContactListWidget(), .contains(lowerValue) ||
defaultSuffixIconColor: scale.primaryScale.border, element.profile.pronouns
closeKeyboardWhenScrolling: true, .toLowerCase()
inputDecoration: InputDecoration( .contains(lowerValue))
labelText: translate('contact_list.search'), .toList();
), },
).paddingAll(8), searchFieldHeight: 40,
).paddingLTRB(8, 0, 8, 8); spaceBetweenSearchAndList: 4,
emptyWidget: const EmptyContactListWidget(),
defaultSuffixIconColor: scale.primaryScale.border,
closeKeyboardWhenScrolling: true,
inputDecoration: InputDecoration(
labelText: translate('contact_list.search'),
),
),
)));
} }
} }

View File

@ -143,7 +143,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators) (scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
? activeBorder ? activeBorder
: null, : null,
borderRadius: 16 * scaleConfig.borderRadiusScale, borderRadius: 12 * scaleConfig.borderRadiusScale,
callback: callback, callback: callback,
footerButtonIcon: loggedIn ? Icons.edit_outlined : null, footerButtonIcon: loggedIn ? Icons.edit_outlined : null,
footerCallback: footerCallback, footerCallback: footerCallback,
@ -197,11 +197,11 @@ class _DrawerMenuState extends State<DrawerMenu> {
loading: () => _wrapInBox( loading: () => _wrapInBox(
child: buildProgressIndicator(), child: buildProgressIndicator(),
color: scaleScheme.grayScale.subtleBorder, color: scaleScheme.grayScale.subtleBorder,
borderRadius: 16 * scaleConfig.borderRadiusScale), borderRadius: 12 * scaleConfig.borderRadiusScale),
error: (err, st) => _wrapInBox( error: (err, st) => _wrapInBox(
child: errorPage(err, st), child: errorPage(err, st),
color: scaleScheme.errorScale.subtleBorder, color: scaleScheme.errorScale.subtleBorder,
borderRadius: 16 * scaleConfig.borderRadiusScale), borderRadius: 12 * scaleConfig.borderRadiusScale),
); );
loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8)); loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8));
} else { } else {
@ -254,7 +254,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
return IconButton( return IconButton(
icon: icon, icon: icon,
color: border, color: border,
constraints: const BoxConstraints.expand(height: 64, width: 64), constraints: const BoxConstraints.expand(height: 48, width: 48),
style: ButtonStyle( style: ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith((states) { backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered)) { if (states.contains(WidgetState.hovered)) {
@ -269,18 +269,18 @@ class _DrawerMenuState extends State<DrawerMenu> {
return RoundedRectangleBorder( return RoundedRectangleBorder(
side: BorderSide(color: hoverBorder, width: 2), side: BorderSide(color: hoverBorder, width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))); Radius.circular(12 * scaleConfig.borderRadiusScale)));
} }
if (states.contains(WidgetState.focused)) { if (states.contains(WidgetState.focused)) {
return RoundedRectangleBorder( return RoundedRectangleBorder(
side: BorderSide(color: activeBorder, width: 2), side: BorderSide(color: activeBorder, width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))); Radius.circular(12 * scaleConfig.borderRadiusScale)));
} }
return RoundedRectangleBorder( return RoundedRectangleBorder(
side: BorderSide(color: border, width: 2), side: BorderSide(color: border, width: 2),
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(16 * scaleConfig.borderRadiusScale))); Radius.circular(12 * scaleConfig.borderRadiusScale)));
})), })),
tooltip: tooltip, tooltip: tooltip,
onPressed: onPressed); onPressed: onPressed);
@ -413,12 +413,18 @@ class _DrawerMenuState extends State<DrawerMenu> {
_getBottomButtons(), _getBottomButtons(),
Row(children: [ Row(children: [
Text('${translate('menu.version')} $packageInfoVersion', Text('${translate('menu.version')} $packageInfoVersion',
style: theme.textTheme.labelMedium! style: theme.textTheme.labelMedium!.copyWith(
.copyWith(color: scale.tertiaryScale.hoverBorder)), color: scaleConfig.preferBorders
? scale.tertiaryScale.hoverBorder
: scale.tertiaryScale.subtleBackground)),
const Spacer(), const Spacer(),
SignalStrengthMeterWidget( SignalStrengthMeterWidget(
color: scale.tertiaryScale.hoverBorder, color: scaleConfig.preferBorders
inactiveColor: scale.tertiaryScale.border, ? scale.tertiaryScale.hoverBorder
: scale.tertiaryScale.subtleBackground,
inactiveColor: scaleConfig.preferBorders
? scale.tertiaryScale.border
: scale.tertiaryScale.elementBackground,
), ),
]) ])
]).paddingAll(16), ]).paddingAll(16),

View File

@ -8,7 +8,6 @@ import 'package:veilid_support/veilid_support.dart';
import '../../account_manager/account_manager.dart'; import '../../account_manager/account_manager.dart';
import '../../theme/theme.dart'; import '../../theme/theme.dart';
import '../../tools/tools.dart';
import 'drawer_menu/drawer_menu.dart'; import 'drawer_menu/drawer_menu.dart';
import 'home_account_invalid.dart'; import 'home_account_invalid.dart';
import 'home_account_locked.dart'; import 'home_account_locked.dart';
@ -37,9 +36,6 @@ class HomeScreenState extends State<HomeScreen>
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount); .indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
final canClose = activeIndex != -1; final canClose = activeIndex != -1;
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.normal);
if (!canClose) { if (!canClose) {
await _zoomDrawerController.open!(); await _zoomDrawerController.open!();
} }
@ -129,7 +125,6 @@ class HomeScreenState extends State<HomeScreen>
final canClose = activeIndex != -1; final canClose = activeIndex != -1;
return SafeArea( return SafeArea(
bottom: false,
child: DefaultTextStyle( child: DefaultTextStyle(
style: theme.textTheme.bodySmall!, style: theme.textTheme.bodySmall!,
child: ZoomDrawer( child: ZoomDrawer(

View File

@ -1,86 +0,0 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../../contact_invitation/contact_invitation.dart';
import '../../../contacts/contacts.dart';
import '../../../theme/theme.dart';
class AccountPage extends StatefulWidget {
const AccountPage({
super.key,
});
@override
AccountPageState createState() => AccountPageState();
}
class AccountPageState extends State<AccountPage> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
final cilState = context.watch<ContactInvitationListCubit>().state;
final cilBusy = cilState.busy;
final contactInvitationRecordList =
cilState.state.asData?.value.map((x) => x.value).toIList() ??
const IListConst([]);
final ciState = context.watch<ContactListCubit>().state;
final ciBusy = ciState.busy;
final contactList =
ciState.state.asData?.value.map((x) => x.value).toIList() ??
const IListConst([]);
return SizedBox(
child: Column(children: <Widget>[
if (contactInvitationRecordList.isNotEmpty)
ExpansionTile(
tilePadding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
backgroundColor: scale.primaryScale.border,
collapsedBackgroundColor: scale.primaryScale.border,
dense: true,
minTileHeight: 16,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
),
collapsedShape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
),
title: Text(
translate('account_page.contact_invitations'),
textAlign: TextAlign.center,
style: textTheme.titleSmall!
.copyWith(color: scale.primaryScale.borderText),
),
iconColor: scale.primaryScale.borderText,
collapsedIconColor: scale.primaryScale.borderText,
initiallyExpanded: true,
children: [
ContactInvitationListWidget(
contactInvitationRecordList: contactInvitationRecordList,
disabled: cilBusy)
],
).paddingLTRB(8, 0, 8, 8),
ContactListWidget(contactList: contactList, disabled: ciBusy).expanded(),
]));
}
}

View File

@ -1,4 +1,3 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../chat_list/chat_list.dart'; import '../../../chat_list/chat_list.dart';
@ -24,8 +23,6 @@ class ChatsPageState extends State<ChatsPage> {
@override @override
// ignore: prefer_expression_function_bodies // ignore: prefer_expression_function_bodies
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column(children: <Widget>[ return const ChatListWidget();
const ChatListWidget().expanded(),
]);
} }
} }

View File

@ -0,0 +1,59 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../contact_invitation/contact_invitation.dart';
import '../../../contacts/contacts.dart';
class ContactsPage extends StatefulWidget {
const ContactsPage({
super.key,
});
@override
ContactsPageState createState() => ContactsPageState();
}
class ContactsPageState extends State<ContactsPage> {
@override
void initState() {
super.initState();
}
@override
void dispose() {
super.dispose();
}
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
// final theme = Theme.of(context);
// final textTheme = theme.textTheme;
// final scale = theme.extension<ScaleScheme>()!;
// final scaleConfig = theme.extension<ScaleConfig>()!;
final cilState = context.watch<ContactInvitationListCubit>().state;
final cilBusy = cilState.busy;
final contactInvitationRecordList =
cilState.state.asData?.value.map((x) => x.value).toIList() ??
const IListConst([]);
final ciState = context.watch<ContactListCubit>().state;
final ciBusy = ciState.busy;
final contactList =
ciState.state.asData?.value.map((x) => x.value).toIList() ??
const IListConst([]);
return CustomScrollView(slivers: [
if (contactInvitationRecordList.isNotEmpty)
SliverPadding(
padding: const EdgeInsets.only(bottom: 8),
sliver: ContactInvitationListWidget(
contactInvitationRecordList: contactInvitationRecordList,
disabled: cilBusy)),
ContactListWidget(contactList: contactList, disabled: ciBusy),
]).paddingLTRB(8, 0, 8, 8);
}
}

View File

@ -13,9 +13,9 @@ import 'package:provider/provider.dart';
import '../../../chat/chat.dart'; import '../../../chat/chat.dart';
import '../../../contact_invitation/contact_invitation.dart'; import '../../../contact_invitation/contact_invitation.dart';
import '../../../theme/theme.dart'; import '../../../theme/theme.dart';
import 'account_page.dart';
import 'bottom_sheet_action_button.dart'; import 'bottom_sheet_action_button.dart';
import 'chats_page.dart'; import 'chats_page.dart';
import 'contacts_page.dart';
class MainPager extends StatefulWidget { class MainPager extends StatefulWidget {
const MainPager({super.key}); const MainPager({super.key});
@ -41,25 +41,6 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
super.dispose(); super.dispose();
} }
bool _onScrollNotification(ScrollNotification notification) {
if (notification is UserScrollNotification &&
notification.metrics.axis == Axis.vertical) {
switch (notification.direction) {
case ScrollDirection.forward:
// _hideBottomBarAnimationController.reverse();
// _fabAnimationController.forward(from: 0);
break;
case ScrollDirection.reverse:
// _hideBottomBarAnimationController.forward();
// _fabAnimationController.reverse(from: 1);
break;
case ScrollDirection.idle:
break;
}
}
return false;
}
Future<void> scanContactInvitationDialog(BuildContext context) async { Future<void> scanContactInvitationDialog(BuildContext context) async {
await showDialog<void>( await showDialog<void>(
context: context, context: context,
@ -162,21 +143,19 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
return Scaffold( return Scaffold(
//extendBody: true, //extendBody: true,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
body: NotificationListener<ScrollNotification>( body: PreloadPageView(
onNotification: _onScrollNotification, key: _pageViewKey,
child: PreloadPageView( controller: pageController,
key: _pageViewKey, preloadPagesCount: 2,
controller: pageController, onPageChanged: (index) {
preloadPagesCount: 2, setState(() {
onPageChanged: (index) { currentPage = index;
setState(() { });
currentPage = index; },
}); children: const [
}, ContactsPage(),
children: const [ ChatsPage(),
AccountPage(), ]),
ChatsPage(),
])),
// appBar: AppBar( // appBar: AppBar(
// toolbarHeight: 24, // toolbarHeight: 24,
// title: Text( // title: Text(
@ -240,7 +219,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
// ]; // ];
final _fabIconList = <IconData>[ final _fabIconList = <IconData>[
Icons.person_add_sharp, Icons.person_add_sharp,
Icons.add_comment_sharp, Icons.chat,
]; ];
final _bottomLabelList = <String>[ final _bottomLabelList = <String>[
translate('pager.contacts'), translate('pager.contacts'),

View File

@ -11,16 +11,11 @@ class Splash extends StatefulWidget {
State<Splash> createState() => _SplashState(); State<Splash> createState() => _SplashState();
} }
class _SplashState extends State<Splash> { class _SplashState extends WindowSetupState<Splash> {
@override _SplashState()
void initState() { : super(
super.initState(); titleBarStyle: TitleBarStyle.hidden,
orientationCapability: OrientationCapability.portraitOnly);
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.hidden, OrientationCapability.normal);
});
}
@override @override
Widget build(BuildContext context) => PopScope( Widget build(BuildContext context) => PopScope(

View File

@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart';
import '../layout/default_app_bar.dart'; import '../layout/default_app_bar.dart';
import '../theme/theme.dart'; import '../theme/theme.dart';
import '../tools/tools.dart';
import '../veilid_processor/veilid_processor.dart'; import '../veilid_processor/veilid_processor.dart';
import 'settings.dart'; import 'settings.dart';
@ -26,11 +25,6 @@ class SettingsPageState extends State<SettingsPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.normal);
});
} }
@override @override

View File

@ -12,7 +12,7 @@ class StyledScaffold extends StatelessWidget {
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!; final scaleConfig = theme.extension<ScaleConfig>()!;
return isDesktop final scaffold = isDesktop
? clipBorder( ? clipBorder(
clipEnabled: true, clipEnabled: true,
borderEnabled: scaleConfig.useVisualIndicators, borderEnabled: scaleConfig.useVisualIndicators,
@ -21,6 +21,10 @@ class StyledScaffold extends StatelessWidget {
child: Scaffold(appBar: appBar, body: body, key: key)) child: Scaffold(appBar: appBar, body: body, key: key))
.paddingAll(32) .paddingAll(32)
: Scaffold(appBar: appBar, body: body, key: key); : Scaffold(appBar: appBar, body: body, key: key);
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: scaffold);
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////

View File

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:async_tools/async_tools.dart'; import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart'; import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
@ -5,8 +7,10 @@ import 'package:blurry_modal_progress_hud/blurry_modal_progress_hud.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:flutter_sticky_header/flutter_sticky_header.dart';
import 'package:motion_toast/motion_toast.dart'; import 'package:motion_toast/motion_toast.dart';
import 'package:quickalert/quickalert.dart'; import 'package:quickalert/quickalert.dart';
import 'package:sliver_expandable/sliver_expandable.dart';
import '../theme.dart'; import '../theme.dart';
@ -132,7 +136,7 @@ void showErrorToast(BuildContext context, String message) {
contentPadding: const EdgeInsets.all(16), contentPadding: const EdgeInsets.all(16),
primaryColor: scale.errorScale.elementBackground, primaryColor: scale.errorScale.elementBackground,
secondaryColor: scale.errorScale.calloutBackground, secondaryColor: scale.errorScale.calloutBackground,
borderRadius: 16 * scaleConfig.borderRadiusScale, borderRadius: 12 * scaleConfig.borderRadiusScale,
toastDuration: const Duration(seconds: 4), toastDuration: const Duration(seconds: 4),
animationDuration: const Duration(milliseconds: 1000), animationDuration: const Duration(milliseconds: 1000),
displayBorder: scaleConfig.useVisualIndicators, displayBorder: scaleConfig.useVisualIndicators,
@ -152,7 +156,7 @@ void showInfoToast(BuildContext context, String message) {
contentPadding: const EdgeInsets.all(16), contentPadding: const EdgeInsets.all(16),
primaryColor: scale.tertiaryScale.elementBackground, primaryColor: scale.tertiaryScale.elementBackground,
secondaryColor: scale.tertiaryScale.calloutBackground, secondaryColor: scale.tertiaryScale.calloutBackground,
borderRadius: 16 * scaleConfig.borderRadiusScale, borderRadius: 12 * scaleConfig.borderRadiusScale,
toastDuration: const Duration(seconds: 2), toastDuration: const Duration(seconds: 2),
animationDuration: const Duration(milliseconds: 500), animationDuration: const Duration(milliseconds: 500),
displayBorder: scaleConfig.useVisualIndicators, displayBorder: scaleConfig.useVisualIndicators,
@ -160,6 +164,159 @@ void showInfoToast(BuildContext context, String message) {
).show(context); ).show(context);
} }
SliverAppBar styledSliverAppBar(
{required BuildContext context, required String title, Color? titleColor}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
//final scaleConfig = theme.extension<ScaleConfig>()!;
final textTheme = theme.textTheme;
return SliverAppBar(
title: Text(
title,
style: textTheme.titleSmall!
.copyWith(color: titleColor ?? scale.primaryScale.borderText),
),
pinned: true,
);
}
Widget styledHeaderSliver(
{required BuildContext context,
required String title,
required Widget sliver,
Color? borderColor,
Color? innerColor,
Color? titleColor,
Color? backgroundColor,
void Function()? onTap}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
final textTheme = theme.textTheme;
return SliverStickyHeader(
header: ColoredBox(
color: backgroundColor ?? Colors.transparent,
child: DecoratedBox(
decoration: ShapeDecoration(
color: borderColor ?? scale.primaryScale.border,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft:
Radius.circular(12 * scaleConfig.borderRadiusScale),
topRight: Radius.circular(
12 * scaleConfig.borderRadiusScale)))),
child: ListTile(
onTap: onTap,
title: Text(title,
textAlign: TextAlign.center,
style: textTheme.titleSmall!.copyWith(
color: titleColor ?? scale.primaryScale.borderText)),
),
)),
sliver: DecoratedSliver(
decoration: ShapeDecoration(
color: borderColor ?? scale.primaryScale.border,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomLeft:
Radius.circular(8 * scaleConfig.borderRadiusScale),
bottomRight:
Radius.circular(8 * scaleConfig.borderRadiusScale)))),
sliver: SliverPadding(
padding: const EdgeInsets.all(4),
sliver: DecoratedSliver(
decoration: ShapeDecoration(
color: innerColor ?? scale.primaryScale.subtleBackground,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale))),
sliver: SliverPadding(
padding: const EdgeInsets.all(8),
sliver: sliver,
)))),
);
}
Widget styledExpandingSliver(
{required BuildContext context,
required String title,
required Widget sliver,
required bool expanded,
required Animation<double> animation,
Color? borderColor,
Color? innerColor,
Color? titleColor,
Color? backgroundColor,
void Function()? onTap}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!;
final textTheme = theme.textTheme;
return SliverStickyHeader(
header: ColoredBox(
color: backgroundColor ?? Colors.transparent,
child: DecoratedBox(
decoration: ShapeDecoration(
color: borderColor ?? scale.primaryScale.border,
shape: RoundedRectangleBorder(
borderRadius: expanded
? BorderRadius.only(
topLeft: Radius.circular(
12 * scaleConfig.borderRadiusScale),
topRight: Radius.circular(
12 * scaleConfig.borderRadiusScale))
: BorderRadius.circular(
12 * scaleConfig.borderRadiusScale))),
child: ListTile(
onTap: onTap,
title: Text(title,
textAlign: TextAlign.center,
style: textTheme.titleSmall!.copyWith(
color: titleColor ?? scale.primaryScale.borderText)),
trailing: AnimatedBuilder(
animation: animation,
builder: (context, child) => Transform.rotate(
angle: (animation.value - 0.5) * pi,
child: child,
),
child: Icon(Icons.chevron_left,
color: borderColor ?? scale.primaryScale.borderText),
),
),
)),
sliver: SliverExpandable(
sliver: DecoratedSliver(
decoration: ShapeDecoration(
color: borderColor ?? scale.primaryScale.border,
shape: RoundedRectangleBorder(
borderRadius: expanded
? BorderRadius.only(
bottomLeft: Radius.circular(
8 * scaleConfig.borderRadiusScale),
bottomRight: Radius.circular(
8 * scaleConfig.borderRadiusScale))
: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale))),
sliver: SliverPadding(
padding: const EdgeInsets.all(4),
sliver: DecoratedSliver(
decoration: ShapeDecoration(
color:
innerColor ?? scale.primaryScale.subtleBackground,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale))),
sliver: SliverPadding(
padding: const EdgeInsets.all(8),
sliver: sliver,
)))),
animation: animation,
));
}
Widget styledTitleContainer({ Widget styledTitleContainer({
required BuildContext context, required BuildContext context,
required String title, required String title,
@ -178,7 +335,7 @@ Widget styledTitleContainer({
color: borderColor ?? scale.primaryScale.border, color: borderColor ?? scale.primaryScale.border,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: borderRadius:
BorderRadius.circular(16 * scaleConfig.borderRadiusScale), BorderRadius.circular(12 * scaleConfig.borderRadiusScale),
)), )),
child: Column(children: [ child: Column(children: [
Text( Text(
@ -192,7 +349,7 @@ Widget styledTitleContainer({
backgroundColor ?? scale.primaryScale.subtleBackground, backgroundColor ?? scale.primaryScale.subtleBackground,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
16 * scaleConfig.borderRadiusScale), 12 * scaleConfig.borderRadiusScale),
)), )),
child: child) child: child)
.paddingAll(4) .paddingAll(4)

View File

@ -0,0 +1,111 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:native_device_orientation/native_device_orientation.dart';
class NativeSafeArea extends StatelessWidget {
const NativeSafeArea({
required this.child,
this.left = true,
this.top = true,
this.right = true,
this.bottom = true,
this.minimum = EdgeInsets.zero,
this.maintainBottomViewPadding = false,
super.key,
});
/// Whether to avoid system intrusions on the left.
final bool left;
/// Whether to avoid system intrusions at the top of the screen, typically the
/// system status bar.
final bool top;
/// Whether to avoid system intrusions on the right.
final bool right;
/// Whether to avoid system intrusions on the bottom side of the screen.
final bool bottom;
/// This minimum padding to apply.
///
/// The greater of the minimum insets and the media padding will be applied.
final EdgeInsets minimum;
/// Specifies whether the [SafeArea] should maintain the bottom
/// [MediaQueryData.viewPadding] instead of the bottom
/// [MediaQueryData.padding], defaults to false.
///
/// For example, if there is an onscreen keyboard displayed above the
/// SafeArea, the padding can be maintained below the obstruction rather than
/// being consumed. This can be helpful in cases where your layout contains
/// flexible widgets, which could visibly move when opening a software
/// keyboard due to the change in the padding value. Setting this to true will
/// avoid the UI shift.
final bool maintainBottomViewPadding;
/// The widget below this widget in the tree.
///
/// The padding on the [MediaQuery] for the [child] will be suitably adjusted
/// to zero out any sides that were avoided by this widget.
///
/// {@macro flutter.widgets.ProxyWidget.child}
final Widget child;
@override
Widget build(BuildContext context) {
final nativeOrientation =
NativeDeviceOrientationReader.orientation(context);
late final bool realLeft;
late final bool realRight;
late final bool realTop;
late final bool realBottom;
switch (nativeOrientation) {
case NativeDeviceOrientation.unknown:
case NativeDeviceOrientation.portraitUp:
realLeft = left;
realRight = right;
realTop = top;
realBottom = bottom;
case NativeDeviceOrientation.portraitDown:
realLeft = right;
realRight = left;
realTop = bottom;
realBottom = top;
case NativeDeviceOrientation.landscapeRight:
realLeft = bottom;
realRight = top;
realTop = left;
realBottom = right;
case NativeDeviceOrientation.landscapeLeft:
realLeft = top;
realRight = bottom;
realTop = right;
realBottom = left;
}
return SafeArea(
left: realLeft,
right: realRight,
top: realTop,
bottom: realBottom,
minimum: minimum,
maintainBottomViewPadding: maintainBottomViewPadding,
child: child);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(DiagnosticsProperty<bool>('left', left))
..add(DiagnosticsProperty<bool>('top', top))
..add(DiagnosticsProperty<bool>('right', right))
..add(DiagnosticsProperty<bool>('bottom', bottom))
..add(DiagnosticsProperty<EdgeInsets>('minimum', minimum))
..add(DiagnosticsProperty<bool>(
'maintainBottomViewPadding', maintainBottomViewPadding));
}
}

View File

@ -1,10 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:async_tools/async_tools.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import '../theme/views/responsive.dart'; import '../theme/views/responsive.dart';
import 'tools.dart';
export 'package:window_manager/window_manager.dart' show TitleBarStyle; export 'package:window_manager/window_manager.dart' show TitleBarStyle;
@ -27,7 +30,7 @@ Future<void> initializeWindowControl() async {
skipTaskbar: false, skipTaskbar: false,
); );
await windowManager.waitUntilReadyToShow(windowOptions, () async { await windowManager.waitUntilReadyToShow(windowOptions, () async {
await changeWindowSetup( await _asyncChangeWindowSetup(
TitleBarStyle.hidden, OrientationCapability.normal); TitleBarStyle.hidden, OrientationCapability.normal);
await windowManager.show(); await windowManager.show();
await windowManager.focus(); await windowManager.focus();
@ -35,7 +38,9 @@ Future<void> initializeWindowControl() async {
} }
} }
Future<void> changeWindowSetup(TitleBarStyle titleBarStyle, const kWindowSetup = '__windowSetup';
Future<void> _asyncChangeWindowSetup(TitleBarStyle titleBarStyle,
OrientationCapability orientationCapability) async { OrientationCapability orientationCapability) async {
if (isDesktop) { if (isDesktop) {
await windowManager.setTitleBarStyle(titleBarStyle); await windowManager.setTitleBarStyle(titleBarStyle);
@ -59,3 +64,47 @@ Future<void> changeWindowSetup(TitleBarStyle titleBarStyle,
} }
} }
} }
void changeWindowSetup(
TitleBarStyle titleBarStyle, OrientationCapability orientationCapability) {
singleFuture<void>(
kWindowSetup,
() async =>
_asyncChangeWindowSetup(titleBarStyle, orientationCapability));
}
abstract class WindowSetupState<T extends StatefulWidget> extends State<T> {
WindowSetupState(
{required this.titleBarStyle, required this.orientationCapability});
@override
void initState() {
changeWindowSetup(this.titleBarStyle, this.orientationCapability);
super.initState();
}
@override
void activate() {
changeWindowSetup(this.titleBarStyle, this.orientationCapability);
super.activate();
}
@override
void deactivate() {
changeWindowSetup(TitleBarStyle.normal, OrientationCapability.normal);
super.deactivate();
}
////////////////////////////////////////////////////////////////////////////
final TitleBarStyle titleBarStyle;
final OrientationCapability orientationCapability;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
..add(EnumProperty<TitleBarStyle>('titleBarStyle', titleBarStyle))
..add(EnumProperty<OrientationCapability>(
'orientationCapability', orientationCapability));
}
}

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:ansicolor/ansicolor.dart'; import 'package:ansicolor/ansicolor.dart';
import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:cool_dropdown/cool_dropdown.dart'; import 'package:cool_dropdown/cool_dropdown.dart';
@ -45,11 +47,6 @@ class _DeveloperPageState extends State<DeveloperPage> {
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await changeWindowSetup(
TitleBarStyle.normal, OrientationCapability.normal);
});
_terminalController.addListener(() { _terminalController.addListener(() {
setState(() {}); setState(() {});
}); });
@ -273,61 +270,59 @@ class _DeveloperPageState extends State<DeveloperPage> {
body: GestureDetector( body: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), onTap: () => FocusScope.of(context).unfocus(),
child: SafeArea( child: SafeArea(
bottom: false,
child: Column(children: [ child: Column(children: [
Stack(alignment: AlignmentDirectional.center, children: [ Stack(alignment: AlignmentDirectional.center, children: [
Image.asset('assets/images/ellet.png'), Image.asset('assets/images/ellet.png'),
TerminalView(globalDebugTerminal, TerminalView(globalDebugTerminal,
textStyle: kDefaultTerminalStyle, textStyle: kDefaultTerminalStyle,
controller: _terminalController, controller: _terminalController,
keyboardType: TextInputType.none, keyboardType: TextInputType.none,
//autofocus: true, //autofocus: true,
backgroundOpacity: _showEllet ? 0.75 : 1.0, backgroundOpacity: _showEllet ? 0.75 : 1.0,
onSecondaryTapDown: (details, offset) async { onSecondaryTapDown: (details, offset) async {
await copySelection(context); await copySelection(context);
}) })
]).expanded(), ]).expanded(),
TextField( TextField(
controller: _debugCommandController, controller: _debugCommandController,
onTapOutside: (event) { onTapOutside: (event) {
FocusManager.instance.primaryFocus?.unfocus(); FocusManager.instance.primaryFocus?.unfocus();
}, },
decoration: InputDecoration( decoration: InputDecoration(
filled: true, filled: true,
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2), contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale), 8 * scaleConfig.borderRadiusScale),
borderSide: BorderSide.none), borderSide: BorderSide.none),
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale), 8 * scaleConfig.borderRadiusScale),
), ),
fillColor: scale.primaryScale.subtleBackground, fillColor: scale.primaryScale.subtleBackground,
hintText: translate('developer.command'), hintText: translate('developer.command'),
suffixIcon: IconButton( suffixIcon: IconButton(
icon: Icon(Icons.send, icon: Icon(Icons.send,
color: _debugCommandController.text.isEmpty color: _debugCommandController.text.isEmpty
? scale.primaryScale.primary.withAlpha(0x3F) ? scale.primaryScale.primary.withAlpha(0x3F)
: scale.primaryScale.primary), : scale.primaryScale.primary),
onPressed: _debugCommandController.text.isEmpty onPressed: _debugCommandController.text.isEmpty
? null ? null
: () async { : () async {
final debugCommand = final debugCommand = _debugCommandController.text;
_debugCommandController.text; _debugCommandController.clear();
_debugCommandController.clear(); await _sendDebugCommand(debugCommand);
await _sendDebugCommand(debugCommand); },
}, )),
)), onChanged: (_) {
onChanged: (_) { setState(() => {});
setState(() => {}); },
}, onSubmitted: (debugCommand) async {
onSubmitted: (debugCommand) async { _debugCommandController.clear();
_debugCommandController.clear(); await _sendDebugCommand(debugCommand);
await _sendDebugCommand(debugCommand); },
}, ).paddingAll(4)
).paddingAll(4) ]))));
]))));
} }
@override @override

View File

@ -9,6 +9,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "67.0.0" version: "67.0.0"
accordion:
dependency: "direct main"
description:
name: accordion
sha256: "0eca3d1c619c6df63d6e384010fd2ef1164e7385d7102f88a6b56f658f160cd0"
url: "https://pub.dev"
source: hosted
version: "2.6.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
@ -449,6 +457,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.5" version: "2.0.5"
expansion_tile_group:
dependency: "direct main"
description:
name: expansion_tile_group
sha256: "6918433891481c7d98cbc604d7b4c93509986e8134d52940853301ad6fbff404"
url: "https://pub.dev"
source: hosted
version: "1.2.4"
fast_immutable_collections: fast_immutable_collections:
dependency: "direct main" dependency: "direct main"
description: description:
@ -620,6 +636,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.2.1" version: "5.2.1"
flutter_sticky_header:
dependency: "direct main"
description:
name: flutter_sticky_header
sha256: "017f398fbb45a589e01491861ca20eb6570a763fd9f3888165a978e11248c709"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
flutter_svg: flutter_svg:
dependency: "direct main" dependency: "direct main"
description: description:
@ -681,6 +705,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.0" version: "4.0.0"
get:
dependency: transitive
description:
name: get
sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e
url: "https://pub.dev"
source: hosted
version: "4.6.6"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@ -897,6 +929,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.10.0" version: "2.10.0"
native_device_orientation:
dependency: "direct main"
description:
name: native_device_orientation
sha256: "0c330c068575e4be72cce5968ca479a3f8d5d1e5dfce7d89d5c13a1e943b338c"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
nested: nested:
dependency: transitive dependency: transitive
description: description:
@ -1326,6 +1366,30 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.99" version: "0.0.99"
sliver_expandable:
dependency: "direct main"
description:
name: sliver_expandable
sha256: ae20eb848bd0ba9dd704732ad654438ac5a5bea2b023fa3cf80a086166d96d97
url: "https://pub.dev"
source: hosted
version: "1.1.1"
sliver_fill_remaining_box_adapter:
dependency: "direct main"
description:
name: sliver_fill_remaining_box_adapter
sha256: "2a222c0f09eb07c37857ce2526c0fbf3b17b2bd1b1ff0e890085f2f7a9ba1927"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
sliver_tools:
dependency: "direct main"
description:
name: sliver_tools
sha256: eae28220badfb9d0559207badcbbc9ad5331aac829a88cb0964d330d2a4636a6
url: "https://pub.dev"
source: hosted
version: "0.2.12"
smart_auth: smart_auth:
dependency: transitive dependency: transitive
description: description:
@ -1583,6 +1647,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.4.0" version: "4.4.0"
value_layout_builder:
dependency: transitive
description:
name: value_layout_builder
sha256: "98202ec1807e94ac72725b7f0d15027afde513c55c69ff3f41bcfccb950831bc"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
vector_graphics: vector_graphics:
dependency: transitive dependency: transitive
description: description:

View File

@ -8,6 +8,7 @@ environment:
flutter: '>=3.22.1' flutter: '>=3.22.1'
dependencies: dependencies:
accordion: ^2.6.0
animated_bottom_navigation_bar: ^1.3.3 animated_bottom_navigation_bar: ^1.3.3
animated_switcher_transitions: ^1.0.0 animated_switcher_transitions: ^1.0.0
animated_theme_switcher: ^2.0.10 animated_theme_switcher: ^2.0.10
@ -27,6 +28,7 @@ dependencies:
cool_dropdown: ^2.1.0 cool_dropdown: ^2.1.0
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
equatable: ^2.0.5 equatable: ^2.0.5
expansion_tile_group: ^1.2.4
fast_immutable_collections: ^10.2.4 fast_immutable_collections: ^10.2.4
file_saver: ^0.2.13 file_saver: ^0.2.13
fixnum: ^1.1.0 fixnum: ^1.1.0
@ -46,6 +48,7 @@ dependencies:
flutter_native_splash: ^2.4.0 flutter_native_splash: ^2.4.0
flutter_slidable: ^3.1.0 flutter_slidable: ^3.1.0
flutter_spinkit: ^5.2.1 flutter_spinkit: ^5.2.1
flutter_sticky_header: ^0.6.5
flutter_svg: ^2.0.10+1 flutter_svg: ^2.0.10+1
flutter_translate: ^4.1.0 flutter_translate: ^4.1.0
flutter_zoom_drawer: ^3.2.0 flutter_zoom_drawer: ^3.2.0
@ -60,6 +63,7 @@ dependencies:
meta: ^1.12.0 meta: ^1.12.0
mobile_scanner: ^5.1.1 mobile_scanner: ^5.1.1
motion_toast: ^2.10.0 motion_toast: ^2.10.0
native_device_orientation: ^2.0.3
package_info_plus: ^8.0.0 package_info_plus: ^8.0.0
pasteboard: ^0.2.0 pasteboard: ^0.2.0
path: ^1.9.0 path: ^1.9.0
@ -81,6 +85,9 @@ dependencies:
share_plus: ^9.0.0 share_plus: ^9.0.0
shared_preferences: ^2.2.3 shared_preferences: ^2.2.3
signal_strength_indicator: ^0.4.1 signal_strength_indicator: ^0.4.1
sliver_expandable: ^1.1.1
sliver_fill_remaining_box_adapter: ^1.0.0
sliver_tools: ^0.2.12
sorted_list: sorted_list:
git: git:
url: https://gitlab.com/veilid/dart-sorted-list-improved.git url: https://gitlab.com/veilid/dart-sorted-list-improved.git