Improved: UI components adapt to native styling

This commit is contained in:
otsmr 2026-05-31 02:22:28 +02:00
parent 9beb8ef9d7
commit 9e28bb82a2
42 changed files with 62 additions and 61 deletions

View file

@ -3,7 +3,8 @@
## 0.2.25 ## 0.2.25
- New: Import images from the gallery - New: Import images from the gallery
- Improves: Media files are now stored in the "twonly" album - Improved: Media files are now stored in the dedicated "twonly" album
- Improved: UI components adapt to native styling (iOS/Android)
- Fix: Migration issue that resulted in a corrupted backup mechanism - Fix: Migration issue that resulted in a corrupted backup mechanism
- Fix: Database issues causing messages to be lost or the database to be corrupted - Fix: Database issues causing messages to be lost or the database to be corrupted
- Fix: Permission view did not disappear after they were granted - Fix: Permission view did not disappear after they were granted

View file

@ -54,7 +54,7 @@ class _VideoPlayerFileHelperState extends State<VideoPlayerFileHelper> {
aspectRatio: _controller.value.aspectRatio, aspectRatio: _controller.value.aspectRatio,
child: VideoPlayerHelper(controller: _controller), child: VideoPlayerHelper(controller: _controller),
) )
: const CircularProgressIndicator(), : const CircularProgressIndicator.adaptive(),
); );
} }
} }

View file

@ -260,7 +260,7 @@ class _StartNewChatView extends State<AddNewShortcutView> {
group: group, group: group,
fontSize: 15, fontSize: 15,
), ),
trailing: Checkbox( trailing: Checkbox.adaptive(
value: _selectedGroups.contains(group.groupId), value: _selectedGroups.contains(group.groupId),
side: WidgetStateBorderSide.resolveWith( side: WidgetStateBorderSide.resolveWith(
(states) { (states) {

View file

@ -75,7 +75,7 @@ class CameraScannedOverlay extends StatelessWidget {
const SizedBox( const SizedBox(
width: 12, width: 12,
height: 12, height: 12,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator.adaptive(strokeWidth: 2),
) )
else else
ColoredBox( ColoredBox(

View file

@ -90,7 +90,7 @@ class SaveToGalleryButtonState extends State<SaveToGalleryButton> {
const SizedBox( const SizedBox(
width: 12, width: 12,
height: 12, height: 12,
child: CircularProgressIndicator(strokeWidth: 1), child: CircularProgressIndicator.adaptive(strokeWidth: 1),
) )
else else
_imageSaved _imageSaved

View file

@ -218,7 +218,7 @@ class _ShareImageView extends State<ShareImageView> {
), ),
Transform.scale( Transform.scale(
scale: 0.75, scale: 0.75,
child: Checkbox( child: Checkbox.adaptive(
value: !hideArchivedUsers, value: !hideArchivedUsers,
side: WidgetStateBorderSide.resolveWith( side: WidgetStateBorderSide.resolveWith(
(states) { (states) {
@ -293,9 +293,9 @@ class _ShareImageView extends State<ShareImageView> {
? SizedBox( ? SizedBox(
height: 12, height: 12,
width: 12, width: 12,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
color: Theme.of(context).colorScheme.inversePrimary, valueColor: AlwaysStoppedAnimation(Theme.of(context).colorScheme.inversePrimary),
), ),
) )
: const FaIcon(FontAwesomeIcons.solidPaperPlane), : const FaIcon(FontAwesomeIcons.solidPaperPlane),
@ -382,7 +382,7 @@ class UserList extends StatelessWidget {
group: group, group: group,
fontSize: 15, fontSize: 15,
), ),
trailing: Checkbox( trailing: Checkbox.adaptive(
value: selectedGroupIds.contains(group.groupId), value: selectedGroupIds.contains(group.groupId),
side: WidgetStateBorderSide.resolveWith( side: WidgetStateBorderSide.resolveWith(
(states) { (states) {

View file

@ -165,7 +165,7 @@ class UserCheckbox extends StatelessWidget {
], ],
), ),
Expanded(child: Container()), Expanded(child: Container()),
Checkbox( Checkbox.adaptive(
value: isChecked, value: isChecked,
side: WidgetStateBorderSide.resolveWith( side: WidgetStateBorderSide.resolveWith(
(states) { (states) {

View file

@ -68,7 +68,7 @@ class _SelectShowTimeState extends State<SelectShowTime> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Checkbox( Checkbox.adaptive(
value: _storeAsDefault, value: _storeAsDefault,
onChanged: (value) => setState(() { onChanged: (value) => setState(() {
_storeAsDefault = !_storeAsDefault; _storeAsDefault = !_storeAsDefault;

View file

@ -717,11 +717,9 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
? SizedBox( ? SizedBox(
height: 12, height: 12,
width: 12, width: 12,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
color: Theme.of( valueColor: AlwaysStoppedAnimation(Theme.of(context).colorScheme.inversePrimary),
context,
).colorScheme.inversePrimary,
), ),
) )
: const FaIcon(FontAwesomeIcons.solidPaperPlane), : const FaIcon(FontAwesomeIcons.solidPaperPlane),

View file

@ -172,7 +172,7 @@ class _DrawLayerState extends State<DrawLayer> {
Positioned.fill( Positioned.fill(
child: RotatedBox( child: RotatedBox(
quarterTurns: 1, quarterTurns: 1,
child: Slider( child: Slider.adaptive(
value: _sliderValue, value: _sliderValue,
thumbColor: currentColor, thumbColor: currentColor,
activeColor: Colors.transparent, activeColor: Colors.transparent,

View file

@ -82,7 +82,7 @@ class TwitterPostCard extends StatelessWidget {
height: 150, height: 150,
color: const Color(0xFFF5F8FA), color: const Color(0xFFF5F8FA),
child: const Center( child: const Center(
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
valueColor: AlwaysStoppedAnimation(twitterBlue), valueColor: AlwaysStoppedAnimation(twitterBlue),
), ),

View file

@ -190,7 +190,7 @@ class _ChatAskAFriendEntryState extends State<ChatAskAFriendEntry> {
child: SizedBox( child: SizedBox(
width: 14, width: 14,
height: 14, height: 14,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
), ),
), ),
@ -302,7 +302,7 @@ class _ChatAskAFriendEntryState extends State<ChatAskAFriendEntry> {
? const SizedBox( ? const SizedBox(
width: 12, width: 12,
height: 12, height: 12,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
), ),
) )

View file

@ -191,7 +191,7 @@ class _ContactRowState extends State<_ContactRow> {
const SizedBox( const SizedBox(
width: 16, width: 16,
height: 16, height: 16,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>( valueColor: AlwaysStoppedAnimation<Color>(
Colors.white, Colors.white,

View file

@ -76,7 +76,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
SizedBox( SizedBox(
width: 10, width: 10,
height: 10, height: 10,
child: CircularProgressIndicator(strokeWidth: 1, color: color), child: CircularProgressIndicator.adaptive(strokeWidth: 1, valueColor: AlwaysStoppedAnimation(color)),
), ),
const SizedBox(width: 2), const SizedBox(width: 2),
], ],

View file

@ -540,7 +540,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
const SizedBox( const SizedBox(
width: 10, width: 10,
height: 10, height: 10,
child: CircularProgressIndicator(strokeWidth: 1), child: CircularProgressIndicator.adaptive(strokeWidth: 1),
) )
else else
imageSaved imageSaved

View file

@ -129,7 +129,7 @@ class _AddContactViaQrLinkViewState extends State<AddContactViaQrLinkView> {
? const SizedBox( ? const SizedBox(
height: 20, height: 20,
width: 20, width: 20,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
), ),
) )

View file

@ -250,7 +250,7 @@ class _SearchUsernameView extends State<AddNewUserView> {
child: SizedBox( child: SizedBox(
width: 18, width: 18,
height: 18, height: 18,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator.adaptive(strokeWidth: 2),
), ),
) )
else else

View file

@ -112,7 +112,7 @@ class FriendSuggestionsComp extends StatelessWidget {
final contact = f.$1; final contact = f.$1;
final isSelected = final isSelected =
selectedFriends.contains(contact.userId); selectedFriends.contains(contact.userId);
return CheckboxListTile( return CheckboxListTile.adaptive(
contentPadding: EdgeInsets.zero, contentPadding: EdgeInsets.zero,
title: Text(contact.displayName ?? contact.username), title: Text(contact.displayName ?? contact.username),
value: isSelected, value: isSelected,

View file

@ -68,7 +68,7 @@ class _GroupCreateSelectGroupNameViewState
? const SizedBox( ? const SizedBox(
width: 15, width: 15,
height: 15, height: 15,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 1, strokeWidth: 1,
), ),
) )

View file

@ -223,7 +223,7 @@ class _StartNewChatView extends State<GroupCreateSelectMembersView> {
contactId: user.userId, contactId: user.userId,
fontSize: 13, fontSize: 13,
), ),
trailing: Checkbox( trailing: Checkbox.adaptive(
value: value:
selectedUsers.contains(user.userId) | selectedUsers.contains(user.userId) |
alreadyInGroup.contains(user.userId), alreadyInGroup.contains(user.userId),

View file

@ -40,9 +40,9 @@ class SynchronizedViewerActionsToolbarComp extends StatelessWidget {
? const SizedBox( ? const SizedBox(
width: 16, width: 16,
height: 16, height: 16,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
color: Colors.white, valueColor: AlwaysStoppedAnimation(Colors.white),
), ),
) )
: const FaIcon( : const FaIcon(

View file

@ -409,7 +409,9 @@ class MemoriesViewState extends State<MemoriesView> {
child: CircularProgressIndicator( child: CircularProgressIndicator(
value: state.migrationProgress, value: state.migrationProgress,
strokeWidth: 2.5, strokeWidth: 2.5,
color: context.color.primary, valueColor: AlwaysStoppedAnimation(
context.color.primary,
),
backgroundColor: context.color.primary backgroundColor: context.color.primary
.withValues(alpha: 0.2), .withValues(alpha: 0.2),
), ),

View file

@ -219,8 +219,8 @@ class _BackupRecoveryViewState extends State<BackupRecoveryView> {
? const SizedBox( ? const SizedBox(
height: 24, height: 24,
width: 24, width: 24,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
color: Colors.white, valueColor: AlwaysStoppedAnimation(Colors.white),
strokeWidth: 3, strokeWidth: 3,
), ),
) )

View file

@ -299,8 +299,8 @@ class _RegisterViewState extends State<RegisterView> {
? const SizedBox( ? const SizedBox(
width: 24, width: 24,
height: 24, height: 24,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
color: Colors.white, valueColor: AlwaysStoppedAnimation(Colors.white),
strokeWidth: 3, strokeWidth: 3,
), ),
) )

View file

@ -49,7 +49,7 @@ class NextButtonComp extends StatelessWidget {
? const SizedBox( ? const SizedBox(
height: 24, height: 24,
width: 24, width: 24,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white), valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
), ),

View file

@ -28,7 +28,7 @@ class SetupSwitchCard extends StatelessWidget {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SwitchListTile( SwitchListTile.adaptive(
value: value, value: value,
onChanged: onChanged, onChanged: onChanged,
title: Text( title: Text(

View file

@ -127,7 +127,7 @@ class _RecoveryViewState extends State<RecoveryView> {
? const SizedBox( ? const SizedBox(
height: 16, height: 16,
width: 16, width: 16,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator.adaptive(strokeWidth: 2),
) )
: const Icon(Icons.restore_rounded), : const Icon(Icons.restore_rounded),
style: FilledButton.styleFrom( style: FilledButton.styleFrom(

View file

@ -144,7 +144,7 @@ class _SetupBackupViewState extends State<SetupBackupView> {
? const SizedBox( ? const SizedBox(
height: 12, height: 12,
width: 12, width: 12,
child: CircularProgressIndicator(strokeWidth: 1), child: CircularProgressIndicator.adaptive(strokeWidth: 1),
) )
: const Icon(Icons.lock_clock_rounded), : const Icon(Icons.lock_clock_rounded),
label: Text( label: Text(

View file

@ -39,7 +39,7 @@ class _ChatSettingsViewState extends State<ChatSettingsView> {
title: Text(context.lang.settingsPreSelectedReactions), title: Text(context.lang.settingsPreSelectedReactions),
onTap: () => context.push(Routes.settingsChatsReactions), onTap: () => context.push(Routes.settingsChatsReactions),
), ),
SwitchListTile( SwitchListTile.adaptive(
title: Text( title: Text(
context context
.lang .lang

View file

@ -213,7 +213,7 @@ class _AutoDownloadOptionsDialogState extends State<AutoDownloadOptionsDialog> {
content: Column( content: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
CheckboxListTile( CheckboxListTile.adaptive(
title: const Text('Image'), title: const Text('Image'),
value: autoDownloadOptions[widget.connectionMode.name]!.contains( value: autoDownloadOptions[widget.connectionMode.name]!.contains(
DownloadMediaTypes.image.name, DownloadMediaTypes.image.name,
@ -222,7 +222,7 @@ class _AutoDownloadOptionsDialogState extends State<AutoDownloadOptionsDialog> {
await _updateAutoDownloadSetting(DownloadMediaTypes.image, value); await _updateAutoDownloadSetting(DownloadMediaTypes.image, value);
}, },
), ),
CheckboxListTile( CheckboxListTile.adaptive(
title: const Text('Video'), title: const Text('Video'),
value: autoDownloadOptions[widget.connectionMode.name]!.contains( value: autoDownloadOptions[widget.connectionMode.name]!.contains(
DownloadMediaTypes.video.name, DownloadMediaTypes.video.name,

View file

@ -379,7 +379,7 @@ class _ImportFromGalleryViewState extends State<ImportFromGalleryView> {
Widget _buildBody() { Widget _buildBody() {
if (_isLoading) { if (_isLoading) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator.adaptive());
} }
if (!_hasPermission) { if (!_hasPermission) {
@ -530,7 +530,7 @@ class _ImportFromGalleryViewState extends State<ImportFromGalleryView> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const CircularProgressIndicator(), const CircularProgressIndicator.adaptive(),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
_importStatus, _importStatus,
@ -600,7 +600,7 @@ class _GalleryThumbnailWidgetState extends State<GalleryThumbnailWidget> {
child: SizedBox( child: SizedBox(
width: 24, width: 24,
height: 24, height: 24,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator.adaptive(strokeWidth: 2),
), ),
), ),
); );

View file

@ -400,7 +400,7 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
? const SizedBox( ? const SizedBox(
width: 24, width: 24,
height: 24, height: 24,
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator.adaptive(strokeWidth: 2),
) )
: null, : null,
onTap: _isGeneratingMockImages onTap: _isGeneratingMockImages

View file

@ -112,7 +112,7 @@ class _ChangeLogViewState extends State<ChangeLogView> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(context.lang.openChangeLog), Text(context.lang.openChangeLog),
Switch( Switch.adaptive(
value: !userService.currentUser.hideChangeLog, value: !userService.currentUser.hideChangeLog,
onChanged: (_) => UserService.update( onChanged: (_) => UserService.update(
(u) => u.hideChangeLog = !u.hideChangeLog, (u) => u.hideChangeLog = !u.hideChangeLog,

View file

@ -243,9 +243,9 @@ $debugLogToken
? SizedBox( ? SizedBox(
height: 12, height: 12,
width: 12, width: 12,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
color: Theme.of(context).colorScheme.inversePrimary, valueColor: AlwaysStoppedAnimation(Theme.of(context).colorScheme.inversePrimary),
), ),
) )
: const FaIcon(FontAwesomeIcons.angleRight), : const FaIcon(FontAwesomeIcons.angleRight),
@ -291,7 +291,7 @@ class _IncludeDebugLogState extends State<IncludeDebugLog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
children: [ children: [
Checkbox( Checkbox.adaptive(
value: widget.isChecked, value: widget.isChecked,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) { onChanged: (value) {

View file

@ -117,7 +117,7 @@ class _FaqViewState extends State<FaqView> {
appBar: AppBar( appBar: AppBar(
title: Text(context.lang.settingsHelpFAQ), title: Text(context.lang.settingsHelpFAQ),
), ),
body: const Center(child: CircularProgressIndicator()), body: const Center(child: CircularProgressIndicator.adaptive()),
); );
} }

View file

@ -123,7 +123,7 @@ class _NotificationViewState extends State<NotificationView> {
? const SizedBox( ? const SizedBox(
width: 16, width: 16,
height: 16, height: 16,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
), ),
) )
@ -138,7 +138,7 @@ class _NotificationViewState extends State<NotificationView> {
? const SizedBox( ? const SizedBox(
width: 16, width: 16,
height: 16, height: 16,
child: CircularProgressIndicator( child: CircularProgressIndicator.adaptive(
strokeWidth: 2, strokeWidth: 2,
), ),
) )

View file

@ -119,7 +119,7 @@ class UserList extends StatelessWidget {
], ],
), ),
leading: AvatarIcon(contactId: user.userId, fontSize: 15), leading: AvatarIcon(contactId: user.userId, fontSize: 15),
trailing: Checkbox( trailing: Checkbox.adaptive(
value: user.blocked, value: user.blocked,
onChanged: (value) async { onChanged: (value) async {
await block(context, user.userId, value); await block(context, user.userId, value);

View file

@ -292,7 +292,7 @@ class UserDiscoverySetupComp extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SwitchListTile( SwitchListTile.adaptive(
value: state.isUserDiscoveryEnabled, value: state.isUserDiscoveryEnabled,
onChanged: (val) => state.update(() { onChanged: (val) => state.update(() {
state.isUserDiscoveryEnabled = val; state.isUserDiscoveryEnabled = val;
@ -323,7 +323,7 @@ class UserDiscoverySetupComp extends StatelessWidget {
), ),
), ),
), ),
SwitchListTile( SwitchListTile.adaptive(
value: state.isManualApprovalEnabled, value: state.isManualApprovalEnabled,
onChanged: (val) => state.update( onChanged: (val) => state.update(
() => state.isManualApprovalEnabled = val, () => state.isManualApprovalEnabled = val,
@ -547,7 +547,7 @@ class UserDiscoverySetupComp extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SwitchListTile( SwitchListTile.adaptive(
value: state.sharePromotion, value: state.sharePromotion,
onChanged: (val) => state.update(() { onChanged: (val) => state.update(() {
state.sharePromotion = val; state.sharePromotion = val;

View file

@ -195,7 +195,7 @@ class _SelectAdditionalUsers extends State<SelectAdditionalUsers> {
contactId: user.userId, contactId: user.userId,
fontSize: 13, fontSize: 13,
), ),
trailing: Checkbox( trailing: Checkbox.adaptive(
value: value:
selectedUsers.contains(user.userId) | selectedUsers.contains(user.userId) |
_alreadySelected.contains(user.userId), _alreadySelected.contains(user.userId),

View file

@ -330,7 +330,7 @@ class _PlanCardState extends State<PlanCard> {
? const SizedBox( ? const SizedBox(
width: 10, width: 10,
height: 10, height: 10,
child: CircularProgressIndicator(strokeWidth: 1), child: CircularProgressIndicator.adaptive(strokeWidth: 1),
) )
: null, : null,
label: Text( label: Text(
@ -350,7 +350,7 @@ class _PlanCardState extends State<PlanCard> {
? const SizedBox( ? const SizedBox(
width: 10, width: 10,
height: 10, height: 10,
child: CircularProgressIndicator(strokeWidth: 1), child: CircularProgressIndicator.adaptive(strokeWidth: 1),
) )
: null, : null,
label: Text( label: Text(

View file

@ -208,7 +208,7 @@ class _SelectAdditionalUsers extends State<SelectContactsView> {
contactId: user.userId, contactId: user.userId,
fontSize: 13, fontSize: 13,
), ),
trailing: Checkbox( trailing: Checkbox.adaptive(
value: value:
selectedUsers.contains(user.userId) | selectedUsers.contains(user.userId) |
_alreadySelected.contains(user.userId), _alreadySelected.contains(user.userId),

View file

@ -176,7 +176,7 @@ class _UserStudyQuestionnaireViewState
'Welche der folgenden Messenger hast du schon einmal benutzt?', 'Welche der folgenden Messenger hast du schon einmal benutzt?',
), ),
..._messengerOptions.map( ..._messengerOptions.map(
(m) => CheckboxListTile( (m) => CheckboxListTile.adaptive(
title: Text(m), title: Text(m),
visualDensity: const VisualDensity(vertical: -4), visualDensity: const VisualDensity(vertical: -4),
value: (_responses['messengers'] as List<dynamic>).contains(m), value: (_responses['messengers'] as List<dynamic>).contains(m),