refact: peer card, orientation (#9235)
* refact: peer card, orientation Signed-off-by: fufesou <linlong1266@gmail.com> * Do not change landscape/portrait on Desktop Signed-off-by: fufesou <linlong1266@gmail.com> * comments Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
parent
39e713838f
commit
d4377a13c5
@ -11,6 +11,7 @@ import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||
import 'package:flutter_hbb/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import 'package:get/get.dart';
|
||||
@ -61,15 +62,16 @@ class _AddressBookState extends State<AddressBook> {
|
||||
retry: null, // remove retry
|
||||
close: () => gFFI.abModel.currentAbPushError.value = ''),
|
||||
Expanded(
|
||||
child: (isDesktop || isWebDesktop)
|
||||
? _buildAddressBookDesktop()
|
||||
: _buildAddressBookMobile())
|
||||
child: Obx(() => stateGlobal.isPortrait.isTrue
|
||||
? _buildAddressBookPortrait()
|
||||
: _buildAddressBookLandscape()),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
Widget _buildAddressBookDesktop() {
|
||||
Widget _buildAddressBookLandscape() {
|
||||
return Row(
|
||||
children: [
|
||||
Offstage(
|
||||
@ -106,7 +108,7 @@ class _AddressBookState extends State<AddressBook> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAddressBookMobile() {
|
||||
Widget _buildAddressBookPortrait() {
|
||||
const padding = 8.0;
|
||||
return Column(
|
||||
children: [
|
||||
@ -239,14 +241,14 @@ class _AddressBookState extends State<AddressBook> {
|
||||
bind.setLocalFlutterOption(k: kOptionCurrentAbName, v: value);
|
||||
}
|
||||
},
|
||||
customButton: Container(
|
||||
height: isDesktop ? 48 : 40,
|
||||
customButton: Obx(()=>Container(
|
||||
height: stateGlobal.isPortrait.isFalse ? 48 : 40,
|
||||
child: Row(children: [
|
||||
Expanded(
|
||||
child: buildItem(gFFI.abModel.currentName.value, button: true)),
|
||||
Icon(Icons.arrow_drop_down),
|
||||
]),
|
||||
),
|
||||
)),
|
||||
underline: Container(
|
||||
height: 0.7,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.1),
|
||||
@ -335,8 +337,8 @@ class _AddressBookState extends State<AddressBook> {
|
||||
showActionMenu: editPermission);
|
||||
}
|
||||
|
||||
final gridView = DynamicGridView.builder(
|
||||
shrinkWrap: isMobile,
|
||||
gridView(bool isPortrait) => DynamicGridView.builder(
|
||||
shrinkWrap: isPortrait,
|
||||
gridDelegate: SliverGridDelegateWithWrapping(),
|
||||
itemCount: tags.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
@ -344,9 +346,9 @@ class _AddressBookState extends State<AddressBook> {
|
||||
return tagBuilder(e);
|
||||
});
|
||||
final maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
|
||||
return (isDesktop || isWebDesktop)
|
||||
? gridView
|
||||
: LimitedBox(maxHeight: maxHeight, child: gridView);
|
||||
return Obx(() => stateGlobal.isPortrait.isFalse
|
||||
? gridView(false)
|
||||
: LimitedBox(maxHeight: maxHeight, child: gridView(true)));
|
||||
});
|
||||
}
|
||||
|
||||
@ -506,9 +508,9 @@ class _AddressBookState extends State<AddressBook> {
|
||||
double marginBottom = 4;
|
||||
|
||||
row({required Widget lable, required Widget input}) {
|
||||
return Row(
|
||||
makeChild(bool isPortrait) => Row(
|
||||
children: [
|
||||
!isMobile
|
||||
!isPortrait
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 100),
|
||||
child: lable.marginOnly(right: 10))
|
||||
@ -519,7 +521,8 @@ class _AddressBookState extends State<AddressBook> {
|
||||
child: input),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: !isMobile ? 8 : 0);
|
||||
).marginOnly(bottom: !isPortrait ? 8 : 0);
|
||||
return Obx(() => makeChild(stateGlobal.isPortrait.isTrue));
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
@ -542,24 +545,24 @@ class _AddressBookState extends State<AddressBook> {
|
||||
),
|
||||
],
|
||||
),
|
||||
input: TextField(
|
||||
input: Obx(() => TextField(
|
||||
controller: idController,
|
||||
inputFormatters: [IDTextInputFormatter()],
|
||||
decoration: InputDecoration(
|
||||
labelText: !isMobile ? null : translate('ID'),
|
||||
labelText: stateGlobal.isPortrait.isFalse ? null : translate('ID'),
|
||||
errorText: errorMsg,
|
||||
errorMaxLines: 5),
|
||||
)),
|
||||
))),
|
||||
row(
|
||||
lable: Text(
|
||||
translate('Alias'),
|
||||
style: style,
|
||||
),
|
||||
input: TextField(
|
||||
input: Obx(() => TextField(
|
||||
controller: aliasController,
|
||||
decoration: InputDecoration(
|
||||
labelText: !isMobile ? null : translate('Alias'),
|
||||
)),
|
||||
labelText: stateGlobal.isPortrait.isFalse ? null : translate('Alias'),
|
||||
),)),
|
||||
),
|
||||
if (isCurrentAbShared)
|
||||
row(
|
||||
@ -567,11 +570,11 @@ class _AddressBookState extends State<AddressBook> {
|
||||
translate('Password'),
|
||||
style: style,
|
||||
),
|
||||
input: TextField(
|
||||
input: Obx(() => TextField(
|
||||
controller: passwordController,
|
||||
obscureText: !passwordVisible,
|
||||
decoration: InputDecoration(
|
||||
labelText: !isMobile ? null : translate('Password'),
|
||||
labelText: stateGlobal.isPortrait.isFalse ? null : translate('Password'),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
passwordVisible
|
||||
@ -585,7 +588,7 @@ class _AddressBookState extends State<AddressBook> {
|
||||
},
|
||||
),
|
||||
),
|
||||
)),
|
||||
),)),
|
||||
if (gFFI.abModel.currentAbTags.isNotEmpty)
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
|
@ -189,7 +189,7 @@ class AutocompletePeerTileState extends State<AutocompletePeerTile> {
|
||||
.map((e) => gFFI.abModel.getCurrentAbTagColor(e))
|
||||
.toList();
|
||||
return Tooltip(
|
||||
message: isMobile
|
||||
message: !(isDesktop || isWebDesktop)
|
||||
? ''
|
||||
: widget.peer.tags.isNotEmpty
|
||||
? '${translate('Tags')}: ${widget.peer.tags.join(', ')}'
|
||||
|
@ -10,6 +10,7 @@ import 'package:flutter_hbb/common/widgets/setting_widgets.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/models/peer_model.dart';
|
||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
|
||||
@ -1123,7 +1124,7 @@ void showRequestElevationDialog(
|
||||
errorText: errPwd.isEmpty ? null : errPwd.value,
|
||||
),
|
||||
],
|
||||
).marginOnly(left: (isDesktop || isWebDesktop) ? 35 : 0),
|
||||
).marginOnly(left: stateGlobal.isPortrait.isFalse ? 35 : 0),
|
||||
).marginOnly(top: 10),
|
||||
],
|
||||
),
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common/hbbs/hbbs.dart';
|
||||
import 'package:flutter_hbb/common/widgets/login.dart';
|
||||
import 'package:flutter_hbb/common/widgets/peers_view.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
@ -45,15 +46,15 @@ class _MyGroupState extends State<MyGroup> {
|
||||
retry: null,
|
||||
close: () => gFFI.groupModel.groupLoadError.value = ''),
|
||||
Expanded(
|
||||
child: (isDesktop || isWebDesktop)
|
||||
? _buildDesktop()
|
||||
: _buildMobile())
|
||||
child: Obx(() => stateGlobal.isPortrait.isTrue
|
||||
? _buildPortrait()
|
||||
: _buildLandscape())),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildDesktop() {
|
||||
Widget _buildLandscape() {
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
@ -89,7 +90,7 @@ class _MyGroupState extends State<MyGroup> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMobile() {
|
||||
Widget _buildPortrait() {
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
@ -159,14 +160,14 @@ class _MyGroupState extends State<MyGroup> {
|
||||
}
|
||||
return true;
|
||||
}).toList();
|
||||
final listView = ListView.builder(
|
||||
shrinkWrap: isMobile,
|
||||
listView(bool isPortrait) => ListView.builder(
|
||||
shrinkWrap: isPortrait,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (context, index) => _buildUserItem(items[index]));
|
||||
var maxHeight = max(MediaQuery.of(context).size.height / 6, 100.0);
|
||||
return (isDesktop || isWebDesktop)
|
||||
? listView
|
||||
: LimitedBox(maxHeight: maxHeight, child: listView);
|
||||
return Obx(() => stateGlobal.isPortrait.isFalse
|
||||
? listView(false)
|
||||
: LimitedBox(maxHeight: maxHeight, child: listView(true)));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common/widgets/dialog.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
@ -53,14 +54,11 @@ class _PeerCardState extends State<_PeerCard>
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
if (isDesktop || isWebDesktop) {
|
||||
return _buildDesktop();
|
||||
} else {
|
||||
return _buildMobile();
|
||||
}
|
||||
return Obx(() =>
|
||||
stateGlobal.isPortrait.isTrue ? _buildPortrait() : _buildLandscape());
|
||||
}
|
||||
|
||||
Widget _buildMobile() {
|
||||
Widget _buildPortrait() {
|
||||
final peer = super.widget.peer;
|
||||
final PeerTabModel peerTabModel = Provider.of(context);
|
||||
return Card(
|
||||
@ -87,7 +85,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
));
|
||||
}
|
||||
|
||||
Widget _buildDesktop() {
|
||||
Widget _buildLandscape() {
|
||||
final PeerTabModel peerTabModel = Provider.of(context);
|
||||
final peer = super.widget.peer;
|
||||
var deco = Rx<BoxDecoration?>(
|
||||
@ -140,13 +138,13 @@ class _PeerCardState extends State<_PeerCard>
|
||||
final greyStyle = TextStyle(
|
||||
fontSize: 11,
|
||||
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
|
||||
final child = Row(
|
||||
makeChild(bool isPortrait) => Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||
borderRadius: isMobile
|
||||
borderRadius: isPortrait
|
||||
? BorderRadius.circular(_tileRadius)
|
||||
: BorderRadius.only(
|
||||
topLeft: Radius.circular(_tileRadius),
|
||||
@ -154,11 +152,11 @@ class _PeerCardState extends State<_PeerCard>
|
||||
),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
width: isMobile ? 50 : 42,
|
||||
height: isMobile ? 50 : null,
|
||||
width: isPortrait ? 50 : 42,
|
||||
height: isPortrait ? 50 : null,
|
||||
child: Stack(
|
||||
children: [
|
||||
getPlatformImage(peer.platform, size: isMobile ? 38 : 30)
|
||||
getPlatformImage(peer.platform, size: isPortrait ? 38 : 30)
|
||||
.paddingAll(6),
|
||||
if (_shouldBuildPasswordIcon(peer))
|
||||
Positioned(
|
||||
@ -183,19 +181,19 @@ class _PeerCardState extends State<_PeerCard>
|
||||
child: Column(
|
||||
children: [
|
||||
Row(children: [
|
||||
getOnline(isMobile ? 4 : 8, peer.online),
|
||||
getOnline(isPortrait ? 4 : 8, peer.online),
|
||||
Expanded(
|
||||
child: Text(
|
||||
peer.alias.isEmpty ? formatID(peer.id) : peer.alias,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
)),
|
||||
]).marginOnly(top: isMobile ? 0 : 2),
|
||||
]).marginOnly(top: isPortrait ? 0 : 2),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
name,
|
||||
style: isMobile ? null : greyStyle,
|
||||
style: isPortrait ? null : greyStyle,
|
||||
textAlign: TextAlign.start,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@ -203,9 +201,9 @@ class _PeerCardState extends State<_PeerCard>
|
||||
],
|
||||
).marginOnly(top: 2),
|
||||
),
|
||||
isMobile
|
||||
? checkBoxOrActionMoreMobile(peer)
|
||||
: checkBoxOrActionMoreDesktop(peer, isTile: true),
|
||||
isPortrait
|
||||
? checkBoxOrActionMorePortrait(peer)
|
||||
: checkBoxOrActionMoreLandscape(peer, isTile: true),
|
||||
],
|
||||
).paddingOnly(left: 10.0, top: 3.0),
|
||||
),
|
||||
@ -216,28 +214,27 @@ class _PeerCardState extends State<_PeerCard>
|
||||
.map((e) => gFFI.abModel.getCurrentAbTagColor(e))
|
||||
.toList();
|
||||
return Tooltip(
|
||||
message: isMobile
|
||||
message: !(isDesktop || isWebDesktop)
|
||||
? ''
|
||||
: peer.tags.isNotEmpty
|
||||
? '${translate('Tags')}: ${peer.tags.join(', ')}'
|
||||
: '',
|
||||
child: Stack(children: [
|
||||
deco == null
|
||||
? child
|
||||
: Obx(
|
||||
() => Container(
|
||||
Obx(() => deco == null
|
||||
? makeChild(stateGlobal.isPortrait.isTrue)
|
||||
: Container(
|
||||
foregroundDecoration: deco.value,
|
||||
child: child,
|
||||
child: makeChild(stateGlobal.isPortrait.isTrue),
|
||||
),
|
||||
),
|
||||
if (colors.isNotEmpty)
|
||||
Positioned(
|
||||
Obx(()=> Positioned(
|
||||
top: 2,
|
||||
right: isMobile ? 20 : 10,
|
||||
right: stateGlobal.isPortrait.isTrue ? 20 : 10,
|
||||
child: CustomPaint(
|
||||
painter: TagPainter(radius: 3, colors: colors),
|
||||
),
|
||||
)
|
||||
))
|
||||
]),
|
||||
);
|
||||
}
|
||||
@ -316,7 +313,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
)),
|
||||
]).paddingSymmetric(vertical: 8)),
|
||||
checkBoxOrActionMoreDesktop(peer, isTile: false),
|
||||
checkBoxOrActionMoreLandscape(peer, isTile: false),
|
||||
],
|
||||
).paddingSymmetric(horizontal: 12.0),
|
||||
)
|
||||
@ -362,7 +359,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
}
|
||||
}
|
||||
|
||||
Widget checkBoxOrActionMoreMobile(Peer peer) {
|
||||
Widget checkBoxOrActionMorePortrait(Peer peer) {
|
||||
final PeerTabModel peerTabModel = Provider.of(context);
|
||||
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||
if (peerTabModel.multiSelectionMode) {
|
||||
@ -390,7 +387,7 @@ class _PeerCardState extends State<_PeerCard>
|
||||
}
|
||||
}
|
||||
|
||||
Widget checkBoxOrActionMoreDesktop(Peer peer, {required bool isTile}) {
|
||||
Widget checkBoxOrActionMoreLandscape(Peer peer, {required bool isTile}) {
|
||||
final PeerTabModel peerTabModel = Provider.of(context);
|
||||
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||
if (peerTabModel.multiSelectionMode) {
|
||||
@ -1257,9 +1254,9 @@ void _rdpDialog(String id) async {
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: isDesktop ? 8 : 0),
|
||||
Row(
|
||||
Obx(() => Row(
|
||||
children: [
|
||||
(isDesktop || isWebDesktop)
|
||||
stateGlobal.isPortrait.isFalse
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
@ -1270,17 +1267,17 @@ void _rdpDialog(String id) async {
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(
|
||||
labelText: (isDesktop || isWebDesktop)
|
||||
labelText: isDesktop
|
||||
? null
|
||||
: translate('Username')),
|
||||
controller: userController,
|
||||
),
|
||||
),
|
||||
],
|
||||
).marginOnly(bottom: (isDesktop || isWebDesktop) ? 8 : 0),
|
||||
Row(
|
||||
).marginOnly(bottom: stateGlobal.isPortrait.isFalse ? 8 : 0)),
|
||||
Obx(() => Row(
|
||||
children: [
|
||||
(isDesktop || isWebDesktop)
|
||||
stateGlobal.isPortrait.isFalse
|
||||
? ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 140),
|
||||
child: Text(
|
||||
@ -1292,7 +1289,7 @@ void _rdpDialog(String id) async {
|
||||
child: Obx(() => TextField(
|
||||
obscureText: secure.value,
|
||||
decoration: InputDecoration(
|
||||
labelText: (isDesktop || isWebDesktop)
|
||||
labelText: isDesktop
|
||||
? null
|
||||
: translate('Password'),
|
||||
suffixIcon: IconButton(
|
||||
@ -1304,7 +1301,7 @@ void _rdpDialog(String id) async {
|
||||
)),
|
||||
),
|
||||
],
|
||||
)
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -16,6 +16,7 @@ import 'package:flutter_hbb/models/ab_model.dart';
|
||||
import 'package:flutter_hbb/models/peer_model.dart';
|
||||
|
||||
import 'package:flutter_hbb/models/peer_tab_model.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -114,26 +115,26 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
textBaseline: TextBaseline.ideographic,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 32,
|
||||
child: Container(
|
||||
padding: (isDesktop || isWebDesktop)
|
||||
? null
|
||||
: EdgeInsets.symmetric(horizontal: 2),
|
||||
child: selectionWrap(Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
visibleContextMenuListener(_createSwitchBar(context))),
|
||||
if (isMobile)
|
||||
..._mobileRightActions(context)
|
||||
else
|
||||
..._desktopRightActions(context)
|
||||
],
|
||||
)),
|
||||
),
|
||||
).paddingOnly(right: (isDesktop || isWebDesktop) ? 12 : 0),
|
||||
Obx(() => SizedBox(
|
||||
height: 32,
|
||||
child: Container(
|
||||
padding: stateGlobal.isPortrait.isTrue
|
||||
? EdgeInsets.symmetric(horizontal: 2)
|
||||
: null,
|
||||
child: selectionWrap(Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: visibleContextMenuListener(
|
||||
_createSwitchBar(context))),
|
||||
if (stateGlobal.isPortrait.isTrue)
|
||||
..._portraitRightActions(context)
|
||||
else
|
||||
..._landscapeRightActions(context)
|
||||
],
|
||||
)),
|
||||
),
|
||||
).paddingOnly(right: stateGlobal.isPortrait.isTrue ? 0 : 12)),
|
||||
_createPeersView(),
|
||||
],
|
||||
);
|
||||
@ -299,7 +300,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
}
|
||||
|
||||
Widget visibleContextMenuListener(Widget child) {
|
||||
if (isMobile) {
|
||||
if (!(isDesktop || isWebDesktop)) {
|
||||
return GestureDetector(
|
||||
onLongPressDown: (e) {
|
||||
final x = e.globalPosition.dx;
|
||||
@ -456,7 +457,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
showToast(translate('Successful'));
|
||||
},
|
||||
child: Icon(PeerTabModel.icons[PeerTabIndex.fav.index]),
|
||||
).marginOnly(left: isMobile ? 11 : 6),
|
||||
).marginOnly(left: !(isDesktop || isWebDesktop) ? 11 : 6),
|
||||
);
|
||||
}
|
||||
|
||||
@ -477,7 +478,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
model.setMultiSelectionMode(false);
|
||||
},
|
||||
child: Icon(PeerTabModel.icons[PeerTabIndex.ab.index]),
|
||||
).marginOnly(left: isMobile ? 11 : 6),
|
||||
).marginOnly(left: !(isDesktop || isWebDesktop) ? 11 : 6),
|
||||
);
|
||||
}
|
||||
|
||||
@ -500,7 +501,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
});
|
||||
},
|
||||
child: Icon(Icons.tag))
|
||||
.marginOnly(left: isMobile ? 11 : 6),
|
||||
.marginOnly(left: !(isDesktop || isWebDesktop) ? 11 : 6),
|
||||
);
|
||||
}
|
||||
|
||||
@ -556,10 +557,10 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _desktopRightActions(BuildContext context) {
|
||||
List<Widget> _landscapeRightActions(BuildContext context) {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
return [
|
||||
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
||||
const PeerSearchBar().marginOnly(right: 13),
|
||||
_createRefresh(
|
||||
index: PeerTabIndex.ab, loading: gFFI.abModel.currentAbLoading),
|
||||
_createRefresh(
|
||||
@ -580,7 +581,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _mobileRightActions(BuildContext context) {
|
||||
List<Widget> _portraitRightActions(BuildContext context) {
|
||||
final model = Provider.of<PeerTabModel>(context);
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final leftIconSize = Theme.of(context).iconTheme.size ?? 24;
|
||||
@ -701,13 +702,13 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
|
||||
baseOffset: 0,
|
||||
extentOffset: peerSearchTextController.value.text.length);
|
||||
});
|
||||
return Container(
|
||||
width: isMobile ? 120 : 140,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Obx(() => Row(
|
||||
return Obx(() => Container(
|
||||
width: stateGlobal.isPortrait.isTrue ? 120 : 140,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
@ -768,8 +769,8 @@ class _PeerSearchBarState extends State<PeerSearchBar> {
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||
import 'package:flutter_hbb/models/state_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
@ -128,7 +129,7 @@ class _PeersViewState extends State<_PeersView>
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
super.didChangeAppLifecycleState(state);
|
||||
if (isDesktop) return;
|
||||
if (isDesktop || isWebDesktop) return;
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
_isActive = true;
|
||||
_queryCount = 0;
|
||||
@ -194,7 +195,7 @@ class _PeersViewState extends State<_PeersView>
|
||||
var peers = snapshot.data!;
|
||||
if (peers.length > 1000) peers = peers.sublist(0, 1000);
|
||||
gFFI.peerTabModel.setCurrentTabCachedPeers(peers);
|
||||
buildOnePeer(Peer peer) {
|
||||
buildOnePeer(Peer peer, bool isPortrait) {
|
||||
final visibilityChild = VisibilityDetector(
|
||||
key: ValueKey(_cardId(peer.id)),
|
||||
onVisibilityChanged: onVisibilityChanged,
|
||||
@ -206,7 +207,7 @@ class _PeersViewState extends State<_PeersView>
|
||||
// No need to listen the currentTab change event.
|
||||
// Because the currentTab change event will trigger the peers change event,
|
||||
// and the peers change event will trigger _buildPeersView().
|
||||
return (isDesktop || isWebDesktop)
|
||||
return !isPortrait
|
||||
? Obx(() => peerCardUiType.value == PeerUiType.list
|
||||
? Container(height: 45, child: visibilityChild)
|
||||
: peerCardUiType.value == PeerUiType.grid
|
||||
@ -217,44 +218,41 @@ class _PeersViewState extends State<_PeersView>
|
||||
: Container(child: visibilityChild);
|
||||
}
|
||||
|
||||
final Widget child;
|
||||
if (isMobile) {
|
||||
child = ListView.builder(
|
||||
itemCount: peers.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildOnePeer(peers[index]).marginOnly(
|
||||
top: index == 0 ? 0 : space / 2, bottom: space / 2);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
child = Obx(() => peerCardUiType.value == PeerUiType.list
|
||||
? DesktopScrollWrapper(
|
||||
scrollController: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
physics: DraggableNeverScrollableScrollPhysics(),
|
||||
itemCount: peers.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildOnePeer(peers[index]).marginOnly(
|
||||
right: space,
|
||||
top: index == 0 ? 0 : space / 2,
|
||||
bottom: space / 2);
|
||||
}),
|
||||
)
|
||||
: DesktopScrollWrapper(
|
||||
scrollController: _scrollController,
|
||||
child: DynamicGridView.builder(
|
||||
controller: _scrollController,
|
||||
physics: DraggableNeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithWrapping(
|
||||
mainAxisSpacing: space / 2,
|
||||
crossAxisSpacing: space),
|
||||
itemCount: peers.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildOnePeer(peers[index]);
|
||||
}),
|
||||
));
|
||||
}
|
||||
final Widget child = Obx(() => stateGlobal.isPortrait.isTrue
|
||||
? ListView.builder(
|
||||
itemCount: peers.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildOnePeer(peers[index], true).marginOnly(
|
||||
top: index == 0 ? 0 : space / 2, bottom: space / 2);
|
||||
},
|
||||
)
|
||||
: peerCardUiType.value == PeerUiType.list
|
||||
? DesktopScrollWrapper(
|
||||
scrollController: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
physics: DraggableNeverScrollableScrollPhysics(),
|
||||
itemCount: peers.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildOnePeer(peers[index], false).marginOnly(
|
||||
right: space,
|
||||
top: index == 0 ? 0 : space / 2,
|
||||
bottom: space / 2);
|
||||
}),
|
||||
)
|
||||
: DesktopScrollWrapper(
|
||||
scrollController: _scrollController,
|
||||
child: DynamicGridView.builder(
|
||||
controller: _scrollController,
|
||||
physics: DraggableNeverScrollableScrollPhysics(),
|
||||
gridDelegate: SliverGridDelegateWithWrapping(
|
||||
mainAxisSpacing: space / 2,
|
||||
crossAxisSpacing: space),
|
||||
itemCount: peers.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return buildOnePeer(peers[index], false);
|
||||
}),
|
||||
));
|
||||
|
||||
if (updateEvent == UpdateEvent.load) {
|
||||
_curPeers.clear();
|
||||
|
@ -243,7 +243,7 @@ class _RawTouchGestureDetectorRegionState
|
||||
if (ffi.cursorModel.shouldBlock(d.localPosition.dx, d.localPosition.dy)) {
|
||||
return;
|
||||
}
|
||||
if (isDesktop) {
|
||||
if (isDesktop || isWebDesktop) {
|
||||
ffi.cursorModel.trySetRemoteWindowCoords();
|
||||
}
|
||||
// Workaround for the issue that the first pan event is sent a long time after the start event.
|
||||
@ -285,7 +285,7 @@ class _RawTouchGestureDetectorRegionState
|
||||
if (lastDeviceKind != PointerDeviceKind.touch) {
|
||||
return;
|
||||
}
|
||||
if (isDesktop) {
|
||||
if (isDesktop || isWebDesktop) {
|
||||
ffi.cursorModel.clearRemoteWindowCoords();
|
||||
}
|
||||
inputModel.sendMouse('up', MouseButtons.left);
|
||||
|
@ -372,7 +372,7 @@ class App extends StatefulWidget {
|
||||
State<App> createState() => _AppState();
|
||||
}
|
||||
|
||||
class _AppState extends State<App> {
|
||||
class _AppState extends State<App> with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -396,6 +396,34 @@ class _AppState extends State<App> {
|
||||
bind.mainChangeTheme(dark: to.toShortString());
|
||||
}
|
||||
};
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _updateOrientation());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
_updateOrientation();
|
||||
}
|
||||
|
||||
void _updateOrientation() {
|
||||
if (isDesktop) return;
|
||||
|
||||
// Don't use `MediaQuery.of(context).orientation` in `didChangeMetrics()`,
|
||||
// my test (Flutter 3.19.6, Android 14) is always the reverse value.
|
||||
// https://github.com/flutter/flutter/issues/60899
|
||||
// stateGlobal.isPortrait.value =
|
||||
// MediaQuery.of(context).orientation == Orientation.portrait;
|
||||
|
||||
final orientation = View.of(context).physicalSize.aspectRatio > 1
|
||||
? Orientation.landscape
|
||||
: Orientation.portrait;
|
||||
stateGlobal.isPortrait.value = orientation == Orientation.portrait;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -20,6 +20,8 @@ class StateGlobal {
|
||||
final svcStatus = SvcStatus.notReady.obs;
|
||||
final RxBool isFocused = false.obs;
|
||||
|
||||
final isPortrait = false.obs;
|
||||
|
||||
String _inputSource = '';
|
||||
|
||||
// Use for desktop -> remote toolbar -> resolution
|
||||
|
Loading…
x
Reference in New Issue
Block a user