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