feat: peer card type
Signed-off-by: Kingtous <kingtous@qq.com>
This commit is contained in:
parent
8a825a7345
commit
4f859d3c9d
@ -5,6 +5,7 @@ import 'package:contextmenu/contextmenu.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/peer_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/peer_widget.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/peercard_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@ -1014,7 +1015,13 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_createTabBar(context),
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: _createTabBar(context)),
|
||||||
|
_createSearchBar(context),
|
||||||
|
_createPeerViewTypeSwitch(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
_createTabBarView(),
|
_createTabBarView(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -1054,4 +1061,46 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
|
|||||||
controller: _tabController, children: super.widget.children)
|
controller: _tabController, children: super.widget.children)
|
||||||
.paddingSymmetric(horizontal: 12.0, vertical: 4.0));
|
.paddingSymmetric(horizontal: 12.0, vertical: 4.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_createSearchBar(BuildContext context) {
|
||||||
|
return Offstage();
|
||||||
|
}
|
||||||
|
|
||||||
|
_createPeerViewTypeSwitch(BuildContext context) {
|
||||||
|
final activeDeco = BoxDecoration(color: Colors.white);
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Obx(
|
||||||
|
() => Container(
|
||||||
|
padding: EdgeInsets.all(4.0),
|
||||||
|
decoration:
|
||||||
|
peerCardUiType.value == PeerUiType.grid ? activeDeco : null,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
peerCardUiType.value = PeerUiType.grid;
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.grid_view_rounded,
|
||||||
|
size: 20,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Obx(
|
||||||
|
() => Container(
|
||||||
|
padding: EdgeInsets.all(4.0),
|
||||||
|
decoration:
|
||||||
|
peerCardUiType.value == PeerUiType.list ? activeDeco : null,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
peerCardUiType.value = PeerUiType.list;
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
Icons.list,
|
||||||
|
size: 20,
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.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';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
|
import '../../common.dart';
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../common.dart';
|
|
||||||
import 'peercard_widget.dart';
|
import 'peercard_widget.dart';
|
||||||
|
|
||||||
typedef OffstageFunc = bool Function(Peer peer);
|
typedef OffstageFunc = bool Function(Peer peer);
|
||||||
@ -82,21 +83,25 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
peers.peers.forEach((peer) {
|
peers.peers.forEach((peer) {
|
||||||
cards.add(Offstage(
|
cards.add(Offstage(
|
||||||
offstage: super.widget._offstageFunc(peer),
|
offstage: super.widget._offstageFunc(peer),
|
||||||
child: Container(
|
child: Obx(
|
||||||
width: 225,
|
() => Container(
|
||||||
height: 150,
|
width: 225,
|
||||||
child: VisibilityDetector(
|
height: peerCardUiType.value == PeerUiType.grid
|
||||||
key: Key('${peer.id}'),
|
? 150
|
||||||
onVisibilityChanged: (info) {
|
: 50,
|
||||||
final peerId = (info.key as ValueKey).value;
|
child: VisibilityDetector(
|
||||||
if (info.visibleFraction > 0.00001) {
|
key: Key('${peer.id}'),
|
||||||
_curPeers.add(peerId);
|
onVisibilityChanged: (info) {
|
||||||
} else {
|
final peerId = (info.key as ValueKey).value;
|
||||||
_curPeers.remove(peerId);
|
if (info.visibleFraction > 0.00001) {
|
||||||
}
|
_curPeers.add(peerId);
|
||||||
_lastChangeTime = DateTime.now();
|
} else {
|
||||||
},
|
_curPeers.remove(peerId);
|
||||||
child: super.widget._peerCardWidgetFunc(peer),
|
}
|
||||||
|
_lastChangeTime = DateTime.now();
|
||||||
|
},
|
||||||
|
child: super.widget._peerCardWidgetFunc(peer),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
});
|
});
|
||||||
@ -162,7 +167,9 @@ class RecentPeerWidget extends BasePeerWidget {
|
|||||||
super._name = "recent peer";
|
super._name = "recent peer";
|
||||||
super._loadEvent = "load_recent_peers";
|
super._loadEvent = "load_recent_peers";
|
||||||
super._offstageFunc = (Peer _peer) => false;
|
super._offstageFunc = (Peer _peer) => false;
|
||||||
super._peerCardWidgetFunc = (Peer peer) => RecentPeerCard(peer: peer);
|
super._peerCardWidgetFunc = (Peer peer) => RecentPeerCard(
|
||||||
|
peer: peer,
|
||||||
|
);
|
||||||
super._initPeers = [];
|
super._initPeers = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,13 +5,17 @@ import 'package:get/get.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
|
import '../../models/platform_model.dart';
|
||||||
|
|
||||||
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
typedef PopupMenuItemsFunc = Future<List<PopupMenuItem<String>>> Function();
|
||||||
|
|
||||||
enum PeerType { recent, fav, discovered, ab }
|
enum PeerType { recent, fav, discovered, ab }
|
||||||
|
|
||||||
|
enum PeerUiType { grid, list }
|
||||||
|
|
||||||
|
final peerCardUiType = PeerUiType.grid.obs;
|
||||||
|
|
||||||
class _PeerCard extends StatefulWidget {
|
class _PeerCard extends StatefulWidget {
|
||||||
final Peer peer;
|
final Peer peer;
|
||||||
final PopupMenuItemsFunc popupMenuItemsFunc;
|
final PopupMenuItemsFunc popupMenuItemsFunc;
|
||||||
@ -39,130 +43,237 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
final peer = super.widget.peer;
|
final peer = super.widget.peer;
|
||||||
var deco = Rx<BoxDecoration?>(BoxDecoration(
|
var deco = Rx<BoxDecoration?>(BoxDecoration(
|
||||||
border: Border.all(color: Colors.transparent, width: 1.0),
|
border: Border.all(color: Colors.transparent, width: 1.0),
|
||||||
borderRadius: BorderRadius.circular(20)));
|
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||||
return Card(
|
? BorderRadius.circular(20)
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
: null));
|
||||||
child: MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (evt) {
|
onEnter: (evt) {
|
||||||
deco.value = BoxDecoration(
|
deco.value = BoxDecoration(
|
||||||
border: Border.all(color: Colors.blue, width: 1.0),
|
border: Border.all(color: Colors.blue, width: 1.0),
|
||||||
borderRadius: BorderRadius.circular(20));
|
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||||
},
|
? BorderRadius.circular(20)
|
||||||
onExit: (evt) {
|
: null);
|
||||||
deco.value = BoxDecoration(
|
},
|
||||||
border: Border.all(color: Colors.transparent, width: 1.0),
|
onExit: (evt) {
|
||||||
borderRadius: BorderRadius.circular(20));
|
deco.value = BoxDecoration(
|
||||||
},
|
border: Border.all(color: Colors.transparent, width: 1.0),
|
||||||
child: GestureDetector(
|
borderRadius: peerCardUiType.value == PeerUiType.grid
|
||||||
onDoubleTap: () => _connect(peer.id),
|
? BorderRadius.circular(20)
|
||||||
child: _buildPeerTile(context, peer, deco)),
|
: null);
|
||||||
));
|
},
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: () => _connect(peer.id),
|
||||||
|
child: Obx(() => peerCardUiType.value == PeerUiType.grid
|
||||||
|
? _buildPeerCard(context, peer, deco)
|
||||||
|
: _buildPeerTile(context, peer, deco))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPeerTile(
|
Widget _buildPeerTile(
|
||||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
|
final greyStyle = TextStyle(fontSize: 12, color: Colors.grey);
|
||||||
return Obx(
|
return Obx(
|
||||||
() => Container(
|
() => Container(
|
||||||
decoration: deco.value,
|
decoration: deco.value,
|
||||||
child: Column(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
width: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||||
|
),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: _getPlatformImage('${peer.platform}').paddingAll(8.0),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(color: Colors.white),
|
||||||
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(20),
|
|
||||||
topRight: Radius.circular(20),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Row(children: [
|
||||||
padding: const EdgeInsets.all(6),
|
Text(
|
||||||
child: _getPlatformImage('${peer.platform}'),
|
'${peer.id}',
|
||||||
),
|
style: TextStyle(fontWeight: FontWeight.w400),
|
||||||
Row(
|
),
|
||||||
children: [
|
Padding(
|
||||||
Expanded(
|
padding: EdgeInsets.fromLTRB(4, 4, 8, 4),
|
||||||
child: FutureBuilder<String>(
|
child: CircleAvatar(
|
||||||
future: bind.mainGetPeerOption(
|
radius: 5,
|
||||||
id: peer.id, key: 'alias'),
|
backgroundColor: peer.online
|
||||||
builder: (_, snapshot) {
|
? Colors.green
|
||||||
if (snapshot.hasData) {
|
: Colors.yellow)),
|
||||||
final name = snapshot.data!.isEmpty
|
]),
|
||||||
? '${peer.username}@${peer.hostname}'
|
Align(
|
||||||
: snapshot.data!;
|
alignment: Alignment.centerLeft,
|
||||||
return Tooltip(
|
child: FutureBuilder<String>(
|
||||||
message: name,
|
future: bind.mainGetPeerOption(
|
||||||
child: Text(
|
id: peer.id, key: 'alias'),
|
||||||
name,
|
builder: (_, snapshot) {
|
||||||
style: TextStyle(
|
if (snapshot.hasData) {
|
||||||
color: Colors.white70,
|
final name = snapshot.data!.isEmpty
|
||||||
fontSize: 12),
|
? '${peer.username}@${peer.hostname}'
|
||||||
textAlign: TextAlign.center,
|
: snapshot.data!;
|
||||||
overflow: TextOverflow.ellipsis,
|
return Tooltip(
|
||||||
),
|
message: name,
|
||||||
);
|
child: Text(
|
||||||
} else {
|
name,
|
||||||
// alias has not arrived
|
style: greyStyle,
|
||||||
return Center(
|
textAlign: TextAlign.start,
|
||||||
child: Text(
|
overflow: TextOverflow.ellipsis,
|
||||||
'${peer.username}@${peer.hostname}',
|
),
|
||||||
style: TextStyle(
|
);
|
||||||
color: Colors.white70,
|
} else {
|
||||||
fontSize: 12),
|
// alias has not arrived
|
||||||
textAlign: TextAlign.center,
|
return Text(
|
||||||
overflow: TextOverflow.ellipsis,
|
'${peer.username}@${peer.hostname}',
|
||||||
));
|
style: greyStyle,
|
||||||
}
|
textAlign: TextAlign.start,
|
||||||
},
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
);
|
||||||
),
|
}
|
||||||
],
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).paddingAll(4.0),
|
),
|
||||||
),
|
),
|
||||||
|
InkWell(
|
||||||
|
child: Icon(Icons.more_vert),
|
||||||
|
onTapDown: (e) {
|
||||||
|
final x = e.globalPosition.dx;
|
||||||
|
final y = e.globalPosition.dy;
|
||||||
|
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
_showPeerMenu(context, peer.id);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
).paddingSymmetric(horizontal: 8.0),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Row(children: [
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0, 4, 8, 4),
|
|
||||||
child: CircleAvatar(
|
|
||||||
radius: 5,
|
|
||||||
backgroundColor:
|
|
||||||
peer.online ? Colors.green : Colors.yellow)),
|
|
||||||
Text('${peer.id}')
|
|
||||||
]),
|
|
||||||
InkWell(
|
|
||||||
child: Icon(Icons.more_vert),
|
|
||||||
onTapDown: (e) {
|
|
||||||
final x = e.globalPosition.dx;
|
|
||||||
final y = e.globalPosition.dy;
|
|
||||||
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
|
||||||
},
|
|
||||||
onTap: () {
|
|
||||||
_showPeerMenu(context, peer.id);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
).paddingSymmetric(vertical: 8.0, horizontal: 12.0)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildPeerCard(
|
||||||
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
|
return Card(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||||
|
child: GestureDetector(
|
||||||
|
onDoubleTap: () => _connect(peer.id),
|
||||||
|
child: Obx(
|
||||||
|
() => Container(
|
||||||
|
decoration: deco.value,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: str2color('${peer.id}${peer.platform}', 0x7f),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(20),
|
||||||
|
topRight: Radius.circular(20),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(6),
|
||||||
|
child: _getPlatformImage('${peer.platform}'),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: FutureBuilder<String>(
|
||||||
|
future: bind.mainGetPeerOption(
|
||||||
|
id: peer.id, key: 'alias'),
|
||||||
|
builder: (_, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
final name = snapshot.data!.isEmpty
|
||||||
|
? '${peer.username}@${peer.hostname}'
|
||||||
|
: snapshot.data!;
|
||||||
|
return Tooltip(
|
||||||
|
message: name,
|
||||||
|
child: Text(
|
||||||
|
name,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white70,
|
||||||
|
fontSize: 12),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// alias has not arrived
|
||||||
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
'${peer.username}@${peer.hostname}',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white70,
|
||||||
|
fontSize: 12),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingAll(4.0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0, 4, 8, 4),
|
||||||
|
child: CircleAvatar(
|
||||||
|
radius: 5,
|
||||||
|
backgroundColor: peer.online
|
||||||
|
? Colors.green
|
||||||
|
: Colors.yellow)),
|
||||||
|
Text('${peer.id}')
|
||||||
|
]),
|
||||||
|
InkWell(
|
||||||
|
child: Icon(Icons.more_vert),
|
||||||
|
onTapDown: (e) {
|
||||||
|
final x = e.globalPosition.dx;
|
||||||
|
final y = e.globalPosition.dy;
|
||||||
|
_menuPos = RelativeRect.fromLTRB(x, y, x, y);
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
_showPeerMenu(context, peer.id);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(vertical: 8.0, horizontal: 12.0)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Connect to a peer with [id].
|
/// Connect to a peer with [id].
|
||||||
/// If [isFileTransfer], starts a session only for file transfer.
|
/// If [isFileTransfer], starts a session only for file transfer.
|
||||||
void _connect(String id, {bool isFileTransfer = false}) async {
|
void _connect(String id, {bool isFileTransfer = false}) async {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user