mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-09-07 23:31:53 +00:00
sliver refactor
This commit is contained in:
parent
67812b3c6f
commit
2fa3cbd21c
@ -97,8 +97,9 @@
|
||||
"invalid_account_title": "Invalid Account",
|
||||
"invalid_account_text": "Account is invalid, removing from list"
|
||||
},
|
||||
"account_page": {
|
||||
"contact_invitations": "Contact Invitations"
|
||||
"contacts_page": {
|
||||
"contacts": "Contacts",
|
||||
"invitations": "Invitations"
|
||||
},
|
||||
"add_contact_sheet": {
|
||||
"new_contact": "New Contact",
|
||||
@ -176,14 +177,13 @@
|
||||
"password_does_not_match": "Password does not match"
|
||||
},
|
||||
"contact_list": {
|
||||
"title": "Contacts",
|
||||
"invite_people": "Invite people to VeilidChat",
|
||||
"search": "Search contacts",
|
||||
"invitation": "Invitation"
|
||||
},
|
||||
"chat_list": {
|
||||
"search": "Search chats",
|
||||
"start_a_conversation": "Start a conversation",
|
||||
"start_a_conversation": "Start A Conversation",
|
||||
"chats": "Chats",
|
||||
"groups": "Groups"
|
||||
},
|
||||
|
@ -58,6 +58,8 @@ PODS:
|
||||
- nanopb/encode (= 2.30910.0)
|
||||
- nanopb/decode (2.30910.0)
|
||||
- nanopb/encode (2.30910.0)
|
||||
- native_device_orientation (0.0.1):
|
||||
- Flutter
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- pasteboard (0.0.1):
|
||||
@ -91,6 +93,7 @@ DEPENDENCIES:
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/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`)
|
||||
- pasteboard (from `.symlinks/plugins/pasteboard/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
@ -129,6 +132,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||
mobile_scanner:
|
||||
:path: ".symlinks/plugins/mobile_scanner/ios"
|
||||
native_device_orientation:
|
||||
:path: ".symlinks/plugins/native_device_orientation/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
pasteboard:
|
||||
@ -169,6 +174,7 @@ SPEC CHECKSUMS:
|
||||
MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1
|
||||
mobile_scanner: 8564358885a9253c43f822435b70f9345c87224f
|
||||
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
||||
native_device_orientation: 348b10c346a60ebbc62fb235a4fdb5d1b61a8f55
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
pasteboard: 982969ebaa7c78af3e6cc7761e8f5e77565d9ce0
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
|
@ -44,25 +44,11 @@ class EditAccountPage extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _EditAccountPageState extends State<EditAccountPage> {
|
||||
bool _isInAsyncCall = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.portraitOnly);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
unawaited(
|
||||
changeWindowSetup(TitleBarStyle.normal, OrientationCapability.normal));
|
||||
super.dispose();
|
||||
}
|
||||
class _EditAccountPageState extends WindowSetupState<EditAccountPage> {
|
||||
_EditAccountPageState()
|
||||
: super(
|
||||
titleBarStyle: TitleBarStyle.normal,
|
||||
orientationCapability: OrientationCapability.portraitOnly);
|
||||
|
||||
Widget _editAccountForm(BuildContext context,
|
||||
{required Future<void> Function(GlobalKey<FormBuilderState>)
|
||||
@ -314,4 +300,8 @@ class _EditAccountPageState extends State<EditAccountPage> {
|
||||
]).paddingSymmetric(horizontal: 24, vertical: 8)))
|
||||
.withModalHUD(context, displayModalHUD);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _isInAsyncCall = false;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -20,18 +22,11 @@ class NewAccountPage extends StatefulWidget {
|
||||
State createState() => _NewAccountPageState();
|
||||
}
|
||||
|
||||
class _NewAccountPageState extends State<NewAccountPage> {
|
||||
bool _isInAsyncCall = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.portraitOnly);
|
||||
});
|
||||
}
|
||||
class _NewAccountPageState extends WindowSetupState<NewAccountPage> {
|
||||
_NewAccountPageState()
|
||||
: super(
|
||||
titleBarStyle: TitleBarStyle.normal,
|
||||
orientationCapability: OrientationCapability.portraitOnly);
|
||||
|
||||
Widget _newAccountForm(BuildContext context,
|
||||
{required Future<void> Function(GlobalKey<FormBuilderState>) onSubmit}) {
|
||||
@ -120,4 +115,8 @@ class _NewAccountPageState extends State<NewAccountPage> {
|
||||
)).paddingSymmetric(horizontal: 24, vertical: 8),
|
||||
).withModalHUD(context, displayModalHUD);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _isInAsyncCall = false;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class ProfileWidget extends StatelessWidget {
|
||||
: scale.primaryScale.borderText,
|
||||
width: 2),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16 * scaleConfig.borderRadiusScale))),
|
||||
Radius.circular(12 * scaleConfig.borderRadiusScale))),
|
||||
),
|
||||
child: Row(children: [
|
||||
const Spacer(),
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
@ -29,22 +30,17 @@ class ShowRecoveryKeyPage extends StatefulWidget {
|
||||
_name = name;
|
||||
|
||||
@override
|
||||
ShowRecoveryKeyPageState createState() => ShowRecoveryKeyPageState();
|
||||
State<ShowRecoveryKeyPage> createState() => _ShowRecoveryKeyPageState();
|
||||
|
||||
final WritableSuperIdentity _writableSuperIdentity;
|
||||
final String _name;
|
||||
}
|
||||
|
||||
class ShowRecoveryKeyPageState extends State<ShowRecoveryKeyPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.portraitOnly);
|
||||
});
|
||||
}
|
||||
class _ShowRecoveryKeyPageState extends WindowSetupState<ShowRecoveryKeyPage> {
|
||||
_ShowRecoveryKeyPageState()
|
||||
: super(
|
||||
titleBarStyle: TitleBarStyle.normal,
|
||||
orientationCapability: OrientationCapability.portraitOnly);
|
||||
|
||||
Future<void> _shareRecoveryKey(
|
||||
BuildContext context, Uint8List recoveryKey, String name) async {
|
||||
|
@ -15,7 +15,6 @@ import 'init.dart';
|
||||
import 'layout/splash.dart';
|
||||
import 'router/router.dart';
|
||||
import 'settings/settings.dart';
|
||||
import 'theme/models/theme_preference.dart';
|
||||
import 'theme/theme.dart';
|
||||
import 'tick.dart';
|
||||
import 'tools/loggy.dart';
|
||||
@ -92,7 +91,7 @@ class VeilidChatApp extends StatelessWidget {
|
||||
Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>(
|
||||
initialData: null,
|
||||
create: (context) async => VeilidChatGlobalInit.initialize(),
|
||||
builder: (context, child) {
|
||||
builder: (context, __) {
|
||||
final globalInit = context.watch<VeilidChatGlobalInit?>();
|
||||
if (globalInit == null) {
|
||||
// Splash screen until we're done with init
|
||||
|
@ -60,36 +60,33 @@ class ChatListWidget extends StatelessWidget {
|
||||
final chatListV = context.watch<ChatListCubit>().state;
|
||||
return chatListV
|
||||
.builder((context, chatList) => SizedBox.expand(
|
||||
child: styledTitleContainer(
|
||||
context: context,
|
||||
title: translate('chat_list.chats'),
|
||||
child: SizedBox.expand(
|
||||
child: (chatList.isEmpty)
|
||||
? const EmptyChatListWidget()
|
||||
: SearchableList<proto.Chat>(
|
||||
initialList: chatList.map((x) => x.value).toList(),
|
||||
itemBuilder: (c) {
|
||||
switch (c.whichKind()) {
|
||||
case proto.Chat_Kind.direct:
|
||||
return _itemBuilderDirect(
|
||||
c.direct,
|
||||
contactMap,
|
||||
contactListV.busy || chatListV.busy);
|
||||
case proto.Chat_Kind.group:
|
||||
return const Text(
|
||||
'group chats not yet supported!');
|
||||
case proto.Chat_Kind.notSet:
|
||||
throw StateError('unknown chat kind');
|
||||
}
|
||||
},
|
||||
filter: (value) =>
|
||||
_itemFilter(contactMap, chatList, value),
|
||||
spaceBetweenSearchAndList: 4,
|
||||
inputDecoration: InputDecoration(
|
||||
labelText: translate('chat_list.search'),
|
||||
),
|
||||
),
|
||||
).paddingAll(8))))
|
||||
child: styledTitleContainer(
|
||||
context: context,
|
||||
title: translate('chat_list.chats'),
|
||||
child: (chatList.isEmpty)
|
||||
? const SizedBox.expand(child: EmptyChatListWidget())
|
||||
: SearchableList<proto.Chat>(
|
||||
initialList: chatList.map((x) => x.value).toList(),
|
||||
itemBuilder: (c) {
|
||||
switch (c.whichKind()) {
|
||||
case proto.Chat_Kind.direct:
|
||||
return _itemBuilderDirect(c.direct, contactMap,
|
||||
contactListV.busy || chatListV.busy);
|
||||
case proto.Chat_Kind.group:
|
||||
return const Text(
|
||||
'group chats not yet supported!');
|
||||
case proto.Chat_Kind.notSet:
|
||||
throw StateError('unknown chat kind');
|
||||
}
|
||||
},
|
||||
filter: (value) =>
|
||||
_itemFilter(contactMap, chatList, value),
|
||||
spaceBetweenSearchAndList: 4,
|
||||
inputDecoration: InputDecoration(
|
||||
labelText: translate('chat_list.search'),
|
||||
),
|
||||
).paddingAll(8),
|
||||
)))
|
||||
.paddingLTRB(8, 0, 8, 8);
|
||||
});
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../theme/theme.dart';
|
||||
@ -31,58 +32,58 @@ class ContactInvitationListWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class ContactInvitationListWidgetState
|
||||
extends State<ContactInvitationListWidget> {
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
extends State<ContactInvitationListWidget>
|
||||
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
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
//final textTheme = theme.textTheme;
|
||||
// final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.fromLTRB(4, 0, 4, 4),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
|
||||
)),
|
||||
constraints: const BoxConstraints(maxHeight: 100),
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
decoration: ShapeDecoration(
|
||||
color: scale.primaryScale.subtleBackground,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
|
||||
)),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
controller: _scrollController,
|
||||
itemCount: widget.contactInvitationRecordList.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < 0 ||
|
||||
index >= widget.contactInvitationRecordList.length) {
|
||||
return null;
|
||||
}
|
||||
return ContactInvitationItemWidget(
|
||||
contactInvitationRecord:
|
||||
widget.contactInvitationRecordList[index],
|
||||
disabled: widget.disabled,
|
||||
key: ObjectKey(widget.contactInvitationRecordList[index]))
|
||||
.paddingLTRB(4, 2, 4, 2);
|
||||
},
|
||||
findChildIndexCallback: (key) {
|
||||
final index = widget.contactInvitationRecordList.indexOf(
|
||||
(key as ObjectKey).value! as proto.ContactInvitationRecord);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return index;
|
||||
},
|
||||
).paddingLTRB(4, 6, 4, 6)),
|
||||
);
|
||||
return styledExpandingSliver(
|
||||
context: context,
|
||||
animation: _animation,
|
||||
expanded: _expanded,
|
||||
backgroundColor: scaleConfig.preferBorders
|
||||
? scale.primaryScale.subtleBackground
|
||||
: scale.primaryScale.subtleBorder,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_expanded = !_expanded;
|
||||
});
|
||||
_controller.animateTo(_expanded ? 1 : 0);
|
||||
},
|
||||
title: translate('contacts_page.invitations'),
|
||||
sliver: SliverList.builder(
|
||||
itemCount: widget.contactInvitationRecordList.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < 0 ||
|
||||
index >= widget.contactInvitationRecordList.length) {
|
||||
return null;
|
||||
}
|
||||
return ContactInvitationItemWidget(
|
||||
contactInvitationRecord:
|
||||
widget.contactInvitationRecordList[index],
|
||||
disabled: widget.disabled,
|
||||
key: ObjectKey(widget.contactInvitationRecordList[index]))
|
||||
.paddingLTRB(4, 2, 4, 2);
|
||||
},
|
||||
findChildIndexCallback: (key) {
|
||||
final index = widget.contactInvitationRecordList.indexOf(
|
||||
(key as ObjectKey).value! as proto.ContactInvitationRecord);
|
||||
if (index == -1) {
|
||||
return null;
|
||||
}
|
||||
return index;
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -28,49 +28,50 @@ class ContactListWidget extends StatefulWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _ContactListWidgetState extends State<ContactListWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
class _ContactListWidgetState extends State<ContactListWidget>
|
||||
with SingleTickerProviderStateMixin {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
//final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
return styledTitleContainer(
|
||||
context: context,
|
||||
title: translate('contact_list.title'),
|
||||
child: SearchableList<proto.Contact>(
|
||||
shrinkWrap: true,
|
||||
initialList: widget.contactList.toList(),
|
||||
itemBuilder: (c) =>
|
||||
ContactItemWidget(contact: c, disabled: widget.disabled)
|
||||
.paddingLTRB(0, 4, 0, 0),
|
||||
filter: (value) {
|
||||
final lowerValue = value.toLowerCase();
|
||||
return widget.contactList
|
||||
.where((element) =>
|
||||
element.nickname.toLowerCase().contains(lowerValue) ||
|
||||
element.profile.name.toLowerCase().contains(lowerValue) ||
|
||||
element.profile.pronouns.toLowerCase().contains(lowerValue))
|
||||
.toList();
|
||||
},
|
||||
searchFieldHeight: 40,
|
||||
spaceBetweenSearchAndList: 4,
|
||||
emptyWidget: const EmptyContactListWidget(),
|
||||
defaultSuffixIconColor: scale.primaryScale.border,
|
||||
closeKeyboardWhenScrolling: true,
|
||||
inputDecoration: InputDecoration(
|
||||
labelText: translate('contact_list.search'),
|
||||
),
|
||||
).paddingAll(8),
|
||||
).paddingLTRB(8, 0, 8, 8);
|
||||
return SliverLayoutBuilder(
|
||||
builder: (context, constraints) => styledHeaderSliver(
|
||||
context: context,
|
||||
backgroundColor: scaleConfig.preferBorders
|
||||
? scale.primaryScale.subtleBackground
|
||||
: scale.primaryScale.subtleBorder,
|
||||
title: translate('contacts_page.contacts'),
|
||||
sliver: SliverFillRemaining(
|
||||
child: SearchableList<proto.Contact>.sliver(
|
||||
initialList: widget.contactList.toList(),
|
||||
itemBuilder: (c) =>
|
||||
ContactItemWidget(contact: c, disabled: widget.disabled)
|
||||
.paddingLTRB(0, 4, 0, 0),
|
||||
filter: (value) {
|
||||
final lowerValue = value.toLowerCase();
|
||||
return widget.contactList
|
||||
.where((element) =>
|
||||
element.nickname.toLowerCase().contains(lowerValue) ||
|
||||
element.profile.name
|
||||
.toLowerCase()
|
||||
.contains(lowerValue) ||
|
||||
element.profile.pronouns
|
||||
.toLowerCase()
|
||||
.contains(lowerValue))
|
||||
.toList();
|
||||
},
|
||||
searchFieldHeight: 40,
|
||||
spaceBetweenSearchAndList: 4,
|
||||
emptyWidget: const EmptyContactListWidget(),
|
||||
defaultSuffixIconColor: scale.primaryScale.border,
|
||||
closeKeyboardWhenScrolling: true,
|
||||
inputDecoration: InputDecoration(
|
||||
labelText: translate('contact_list.search'),
|
||||
),
|
||||
),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
|
||||
? activeBorder
|
||||
: null,
|
||||
borderRadius: 16 * scaleConfig.borderRadiusScale,
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale,
|
||||
callback: callback,
|
||||
footerButtonIcon: loggedIn ? Icons.edit_outlined : null,
|
||||
footerCallback: footerCallback,
|
||||
@ -197,11 +197,11 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
loading: () => _wrapInBox(
|
||||
child: buildProgressIndicator(),
|
||||
color: scaleScheme.grayScale.subtleBorder,
|
||||
borderRadius: 16 * scaleConfig.borderRadiusScale),
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale),
|
||||
error: (err, st) => _wrapInBox(
|
||||
child: errorPage(err, st),
|
||||
color: scaleScheme.errorScale.subtleBorder,
|
||||
borderRadius: 16 * scaleConfig.borderRadiusScale),
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale),
|
||||
);
|
||||
loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8));
|
||||
} else {
|
||||
@ -254,7 +254,7 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
return IconButton(
|
||||
icon: icon,
|
||||
color: border,
|
||||
constraints: const BoxConstraints.expand(height: 64, width: 64),
|
||||
constraints: const BoxConstraints.expand(height: 48, width: 48),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.hovered)) {
|
||||
@ -269,18 +269,18 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
return RoundedRectangleBorder(
|
||||
side: BorderSide(color: hoverBorder, width: 2),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16 * scaleConfig.borderRadiusScale)));
|
||||
Radius.circular(12 * scaleConfig.borderRadiusScale)));
|
||||
}
|
||||
if (states.contains(WidgetState.focused)) {
|
||||
return RoundedRectangleBorder(
|
||||
side: BorderSide(color: activeBorder, width: 2),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16 * scaleConfig.borderRadiusScale)));
|
||||
Radius.circular(12 * scaleConfig.borderRadiusScale)));
|
||||
}
|
||||
return RoundedRectangleBorder(
|
||||
side: BorderSide(color: border, width: 2),
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16 * scaleConfig.borderRadiusScale)));
|
||||
Radius.circular(12 * scaleConfig.borderRadiusScale)));
|
||||
})),
|
||||
tooltip: tooltip,
|
||||
onPressed: onPressed);
|
||||
@ -413,12 +413,18 @@ class _DrawerMenuState extends State<DrawerMenu> {
|
||||
_getBottomButtons(),
|
||||
Row(children: [
|
||||
Text('${translate('menu.version')} $packageInfoVersion',
|
||||
style: theme.textTheme.labelMedium!
|
||||
.copyWith(color: scale.tertiaryScale.hoverBorder)),
|
||||
style: theme.textTheme.labelMedium!.copyWith(
|
||||
color: scaleConfig.preferBorders
|
||||
? scale.tertiaryScale.hoverBorder
|
||||
: scale.tertiaryScale.subtleBackground)),
|
||||
const Spacer(),
|
||||
SignalStrengthMeterWidget(
|
||||
color: scale.tertiaryScale.hoverBorder,
|
||||
inactiveColor: scale.tertiaryScale.border,
|
||||
color: scaleConfig.preferBorders
|
||||
? scale.tertiaryScale.hoverBorder
|
||||
: scale.tertiaryScale.subtleBackground,
|
||||
inactiveColor: scaleConfig.preferBorders
|
||||
? scale.tertiaryScale.border
|
||||
: scale.tertiaryScale.elementBackground,
|
||||
),
|
||||
])
|
||||
]).paddingAll(16),
|
||||
|
@ -8,7 +8,6 @@ import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import 'drawer_menu/drawer_menu.dart';
|
||||
import 'home_account_invalid.dart';
|
||||
import 'home_account_locked.dart';
|
||||
@ -37,9 +36,6 @@ class HomeScreenState extends State<HomeScreen>
|
||||
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
|
||||
final canClose = activeIndex != -1;
|
||||
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.normal);
|
||||
|
||||
if (!canClose) {
|
||||
await _zoomDrawerController.open!();
|
||||
}
|
||||
@ -129,7 +125,6 @@ class HomeScreenState extends State<HomeScreen>
|
||||
final canClose = activeIndex != -1;
|
||||
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: DefaultTextStyle(
|
||||
style: theme.textTheme.bodySmall!,
|
||||
child: ZoomDrawer(
|
||||
|
@ -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(),
|
||||
]));
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../chat_list/chat_list.dart';
|
||||
@ -24,8 +23,6 @@ class ChatsPageState extends State<ChatsPage> {
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
return Column(children: <Widget>[
|
||||
const ChatListWidget().expanded(),
|
||||
]);
|
||||
return const ChatListWidget();
|
||||
}
|
||||
}
|
||||
|
59
lib/layout/home/main_pager/contacts_page.dart
Normal file
59
lib/layout/home/main_pager/contacts_page.dart
Normal 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);
|
||||
}
|
||||
}
|
@ -13,9 +13,9 @@ import 'package:provider/provider.dart';
|
||||
import '../../../chat/chat.dart';
|
||||
import '../../../contact_invitation/contact_invitation.dart';
|
||||
import '../../../theme/theme.dart';
|
||||
import 'account_page.dart';
|
||||
import 'bottom_sheet_action_button.dart';
|
||||
import 'chats_page.dart';
|
||||
import 'contacts_page.dart';
|
||||
|
||||
class MainPager extends StatefulWidget {
|
||||
const MainPager({super.key});
|
||||
@ -41,25 +41,6 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
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 {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
@ -162,21 +143,19 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
return Scaffold(
|
||||
//extendBody: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
body: NotificationListener<ScrollNotification>(
|
||||
onNotification: _onScrollNotification,
|
||||
child: PreloadPageView(
|
||||
key: _pageViewKey,
|
||||
controller: pageController,
|
||||
preloadPagesCount: 2,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
currentPage = index;
|
||||
});
|
||||
},
|
||||
children: const [
|
||||
AccountPage(),
|
||||
ChatsPage(),
|
||||
])),
|
||||
body: PreloadPageView(
|
||||
key: _pageViewKey,
|
||||
controller: pageController,
|
||||
preloadPagesCount: 2,
|
||||
onPageChanged: (index) {
|
||||
setState(() {
|
||||
currentPage = index;
|
||||
});
|
||||
},
|
||||
children: const [
|
||||
ContactsPage(),
|
||||
ChatsPage(),
|
||||
]),
|
||||
// appBar: AppBar(
|
||||
// toolbarHeight: 24,
|
||||
// title: Text(
|
||||
@ -240,7 +219,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
// ];
|
||||
final _fabIconList = <IconData>[
|
||||
Icons.person_add_sharp,
|
||||
Icons.add_comment_sharp,
|
||||
Icons.chat,
|
||||
];
|
||||
final _bottomLabelList = <String>[
|
||||
translate('pager.contacts'),
|
||||
|
@ -11,16 +11,11 @@ class Splash extends StatefulWidget {
|
||||
State<Splash> createState() => _SplashState();
|
||||
}
|
||||
|
||||
class _SplashState extends State<Splash> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.hidden, OrientationCapability.normal);
|
||||
});
|
||||
}
|
||||
class _SplashState extends WindowSetupState<Splash> {
|
||||
_SplashState()
|
||||
: super(
|
||||
titleBarStyle: TitleBarStyle.hidden,
|
||||
orientationCapability: OrientationCapability.portraitOnly);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => PopScope(
|
||||
|
@ -7,7 +7,6 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../layout/default_app_bar.dart';
|
||||
import '../theme/theme.dart';
|
||||
import '../tools/tools.dart';
|
||||
import '../veilid_processor/veilid_processor.dart';
|
||||
import 'settings.dart';
|
||||
|
||||
@ -26,11 +25,6 @@ class SettingsPageState extends State<SettingsPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.normal);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -12,7 +12,7 @@ class StyledScaffold extends StatelessWidget {
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final scaleConfig = theme.extension<ScaleConfig>()!;
|
||||
|
||||
return isDesktop
|
||||
final scaffold = isDesktop
|
||||
? clipBorder(
|
||||
clipEnabled: true,
|
||||
borderEnabled: scaleConfig.useVisualIndicators,
|
||||
@ -21,6 +21,10 @@ class StyledScaffold extends StatelessWidget {
|
||||
child: Scaffold(appBar: appBar, body: body, key: key))
|
||||
.paddingAll(32)
|
||||
: Scaffold(appBar: appBar, body: body, key: key);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
child: scaffold);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:awesome_extensions/awesome_extensions.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_bloc/flutter_bloc.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:quickalert/quickalert.dart';
|
||||
import 'package:sliver_expandable/sliver_expandable.dart';
|
||||
|
||||
import '../theme.dart';
|
||||
|
||||
@ -132,7 +136,7 @@ void showErrorToast(BuildContext context, String message) {
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
primaryColor: scale.errorScale.elementBackground,
|
||||
secondaryColor: scale.errorScale.calloutBackground,
|
||||
borderRadius: 16 * scaleConfig.borderRadiusScale,
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale,
|
||||
toastDuration: const Duration(seconds: 4),
|
||||
animationDuration: const Duration(milliseconds: 1000),
|
||||
displayBorder: scaleConfig.useVisualIndicators,
|
||||
@ -152,7 +156,7 @@ void showInfoToast(BuildContext context, String message) {
|
||||
contentPadding: const EdgeInsets.all(16),
|
||||
primaryColor: scale.tertiaryScale.elementBackground,
|
||||
secondaryColor: scale.tertiaryScale.calloutBackground,
|
||||
borderRadius: 16 * scaleConfig.borderRadiusScale,
|
||||
borderRadius: 12 * scaleConfig.borderRadiusScale,
|
||||
toastDuration: const Duration(seconds: 2),
|
||||
animationDuration: const Duration(milliseconds: 500),
|
||||
displayBorder: scaleConfig.useVisualIndicators,
|
||||
@ -160,6 +164,159 @@ void showInfoToast(BuildContext context, String message) {
|
||||
).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({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
@ -178,7 +335,7 @@ Widget styledTitleContainer({
|
||||
color: borderColor ?? scale.primaryScale.border,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(16 * scaleConfig.borderRadiusScale),
|
||||
BorderRadius.circular(12 * scaleConfig.borderRadiusScale),
|
||||
)),
|
||||
child: Column(children: [
|
||||
Text(
|
||||
@ -192,7 +349,7 @@ Widget styledTitleContainer({
|
||||
backgroundColor ?? scale.primaryScale.subtleBackground,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
16 * scaleConfig.borderRadiusScale),
|
||||
12 * scaleConfig.borderRadiusScale),
|
||||
)),
|
||||
child: child)
|
||||
.paddingAll(4)
|
||||
|
111
lib/tools/native_safe_area.dart
Normal file
111
lib/tools/native_safe_area.dart
Normal 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));
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../theme/views/responsive.dart';
|
||||
import 'tools.dart';
|
||||
|
||||
export 'package:window_manager/window_manager.dart' show TitleBarStyle;
|
||||
|
||||
@ -27,7 +30,7 @@ Future<void> initializeWindowControl() async {
|
||||
skipTaskbar: false,
|
||||
);
|
||||
await windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||
await changeWindowSetup(
|
||||
await _asyncChangeWindowSetup(
|
||||
TitleBarStyle.hidden, OrientationCapability.normal);
|
||||
await windowManager.show();
|
||||
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 {
|
||||
if (isDesktop) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:ansicolor/ansicolor.dart';
|
||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:cool_dropdown/cool_dropdown.dart';
|
||||
@ -45,11 +47,6 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await changeWindowSetup(
|
||||
TitleBarStyle.normal, OrientationCapability.normal);
|
||||
});
|
||||
|
||||
_terminalController.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
@ -273,61 +270,59 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
body: GestureDetector(
|
||||
onTap: () => FocusScope.of(context).unfocus(),
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(children: [
|
||||
Stack(alignment: AlignmentDirectional.center, children: [
|
||||
Image.asset('assets/images/ellet.png'),
|
||||
TerminalView(globalDebugTerminal,
|
||||
textStyle: kDefaultTerminalStyle,
|
||||
controller: _terminalController,
|
||||
keyboardType: TextInputType.none,
|
||||
//autofocus: true,
|
||||
backgroundOpacity: _showEllet ? 0.75 : 1.0,
|
||||
onSecondaryTapDown: (details, offset) async {
|
||||
await copySelection(context);
|
||||
})
|
||||
]).expanded(),
|
||||
TextField(
|
||||
controller: _debugCommandController,
|
||||
onTapOutside: (event) {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
8 * scaleConfig.borderRadiusScale),
|
||||
borderSide: BorderSide.none),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
8 * scaleConfig.borderRadiusScale),
|
||||
),
|
||||
fillColor: scale.primaryScale.subtleBackground,
|
||||
hintText: translate('developer.command'),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(Icons.send,
|
||||
color: _debugCommandController.text.isEmpty
|
||||
? scale.primaryScale.primary.withAlpha(0x3F)
|
||||
: scale.primaryScale.primary),
|
||||
onPressed: _debugCommandController.text.isEmpty
|
||||
? null
|
||||
: () async {
|
||||
final debugCommand =
|
||||
_debugCommandController.text;
|
||||
_debugCommandController.clear();
|
||||
await _sendDebugCommand(debugCommand);
|
||||
},
|
||||
)),
|
||||
onChanged: (_) {
|
||||
setState(() => {});
|
||||
},
|
||||
onSubmitted: (debugCommand) async {
|
||||
_debugCommandController.clear();
|
||||
await _sendDebugCommand(debugCommand);
|
||||
},
|
||||
).paddingAll(4)
|
||||
]))));
|
||||
Stack(alignment: AlignmentDirectional.center, children: [
|
||||
Image.asset('assets/images/ellet.png'),
|
||||
TerminalView(globalDebugTerminal,
|
||||
textStyle: kDefaultTerminalStyle,
|
||||
controller: _terminalController,
|
||||
keyboardType: TextInputType.none,
|
||||
//autofocus: true,
|
||||
backgroundOpacity: _showEllet ? 0.75 : 1.0,
|
||||
onSecondaryTapDown: (details, offset) async {
|
||||
await copySelection(context);
|
||||
})
|
||||
]).expanded(),
|
||||
TextField(
|
||||
controller: _debugCommandController,
|
||||
onTapOutside: (event) {
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
8 * scaleConfig.borderRadiusScale),
|
||||
borderSide: BorderSide.none),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
8 * scaleConfig.borderRadiusScale),
|
||||
),
|
||||
fillColor: scale.primaryScale.subtleBackground,
|
||||
hintText: translate('developer.command'),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(Icons.send,
|
||||
color: _debugCommandController.text.isEmpty
|
||||
? scale.primaryScale.primary.withAlpha(0x3F)
|
||||
: scale.primaryScale.primary),
|
||||
onPressed: _debugCommandController.text.isEmpty
|
||||
? null
|
||||
: () async {
|
||||
final debugCommand = _debugCommandController.text;
|
||||
_debugCommandController.clear();
|
||||
await _sendDebugCommand(debugCommand);
|
||||
},
|
||||
)),
|
||||
onChanged: (_) {
|
||||
setState(() => {});
|
||||
},
|
||||
onSubmitted: (debugCommand) async {
|
||||
_debugCommandController.clear();
|
||||
await _sendDebugCommand(debugCommand);
|
||||
},
|
||||
).paddingAll(4)
|
||||
]))));
|
||||
}
|
||||
|
||||
@override
|
||||
|
72
pubspec.lock
72
pubspec.lock
@ -9,6 +9,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "67.0.0"
|
||||
accordion:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: accordion
|
||||
sha256: "0eca3d1c619c6df63d6e384010fd2ef1164e7385d7102f88a6b56f658f160cd0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -449,6 +457,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -620,6 +636,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -681,6 +705,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
get:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: get
|
||||
sha256: e4e7335ede17452b391ed3b2ede016545706c01a02292a6c97619705e7d2a85e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.6.6"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -897,6 +929,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1326,6 +1366,30 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1583,6 +1647,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -8,6 +8,7 @@ environment:
|
||||
flutter: '>=3.22.1'
|
||||
|
||||
dependencies:
|
||||
accordion: ^2.6.0
|
||||
animated_bottom_navigation_bar: ^1.3.3
|
||||
animated_switcher_transitions: ^1.0.0
|
||||
animated_theme_switcher: ^2.0.10
|
||||
@ -27,6 +28,7 @@ dependencies:
|
||||
cool_dropdown: ^2.1.0
|
||||
cupertino_icons: ^1.0.8
|
||||
equatable: ^2.0.5
|
||||
expansion_tile_group: ^1.2.4
|
||||
fast_immutable_collections: ^10.2.4
|
||||
file_saver: ^0.2.13
|
||||
fixnum: ^1.1.0
|
||||
@ -46,6 +48,7 @@ dependencies:
|
||||
flutter_native_splash: ^2.4.0
|
||||
flutter_slidable: ^3.1.0
|
||||
flutter_spinkit: ^5.2.1
|
||||
flutter_sticky_header: ^0.6.5
|
||||
flutter_svg: ^2.0.10+1
|
||||
flutter_translate: ^4.1.0
|
||||
flutter_zoom_drawer: ^3.2.0
|
||||
@ -60,6 +63,7 @@ dependencies:
|
||||
meta: ^1.12.0
|
||||
mobile_scanner: ^5.1.1
|
||||
motion_toast: ^2.10.0
|
||||
native_device_orientation: ^2.0.3
|
||||
package_info_plus: ^8.0.0
|
||||
pasteboard: ^0.2.0
|
||||
path: ^1.9.0
|
||||
@ -81,6 +85,9 @@ dependencies:
|
||||
share_plus: ^9.0.0
|
||||
shared_preferences: ^2.2.3
|
||||
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:
|
||||
git:
|
||||
url: https://gitlab.com/veilid/dart-sorted-list-improved.git
|
||||
|
Loading…
Reference in New Issue
Block a user