optimize style of peer card

Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
21pages 2022-08-24 11:01:58 +08:00
parent 7c9f799f05
commit bb64690ac9
8 changed files with 363 additions and 310 deletions

Binary file not shown.

View File

@ -41,15 +41,18 @@ late final iconRestart = MemoryImage(Uint8List.fromList(base64Decode(
'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAB7BAAAewQHDaVRTAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAbhJREFUWIXVlrFqFGEUhb+7UYxaWCQKlrKKxaZSQVGDJih2tj6MD2DnMwiWvoAIRnENIpZiYxEro6IooiS7SPwsMgNLkk3mjmYmnmb45/73nMNwz/x/qH3gMu2gH6rAU+Blw+Lngau4jpmGxVF7qp1iPWjaQKnZ2WnXbuP/NqAeUPc3ZkA9XDwvqc+BVWCgPlJ7tRwUKThZce819b46VH+pfXVRXVO/q2cSul3VOgZUl0ejq86r39TXI8mqZKDuDEwCw3IREQvAbWAGmMsQZQ0sAl3gHPB1Q+0e8BuYzRDuy2yOiFVgaUxtRf0ETGc4syk4rc6PqU0Cx9j8Zf6dAeAK8Fi9sUXtFjABvEgxJlNwRP2svlNPjbw/q35U36oTFbnyMSwabxb/gB/qA3VBHagrauV7RW0DRfP1IvMlXqkXkhz1DYyQTKtHa/Z2VVMx3IiI+PI3/bCHjuOpFrSnAMpL6QfgTcMGesDx0kBr2BMzsNyi/vtQu8CJlgwsRbZDnWP90NkKaxHxJMOXMqAeAn5u0ydwMCKGY+qbkB3C2W3EKWoXk5zVoHbUZ+6Mh7tl4G4F8RJ3qvL+AfV3r5Vdpj70AAAAAElFTkSuQmCC')));
class IconFont {
static const _family = 'iconfont';
static const _family1 = 'Tabbar';
static const _family2 = 'PeerSearchbar';
IconFont._();
static const IconData max = IconData(0xe606, fontFamily: _family);
static const IconData restore = IconData(0xe607, fontFamily: _family);
static const IconData close = IconData(0xe668, fontFamily: _family);
static const IconData min = IconData(0xe609, fontFamily: _family);
static const IconData add = IconData(0xe664, fontFamily: _family);
static const IconData menu = IconData(0xe628, fontFamily: _family);
static const IconData max = IconData(0xe606, fontFamily: _family1);
static const IconData restore = IconData(0xe607, fontFamily: _family1);
static const IconData close = IconData(0xe668, fontFamily: _family1);
static const IconData min = IconData(0xe609, fontFamily: _family1);
static const IconData add = IconData(0xe664, fontFamily: _family1);
static const IconData menu = IconData(0xe628, fontFamily: _family1);
static const IconData search = IconData(0xe6a4, fontFamily: _family2);
static const IconData round_close = IconData(0xe6ed, fontFamily: _family2);
}
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {

View File

@ -63,20 +63,18 @@ class _ConnectionPageState extends State<ConnectionPage> {
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Column(
children: [
getUpdateUI(),
Row(
children: [
getSearchBarUI(context),
],
).marginOnly(top: 22, left: 22),
).marginOnly(top: 22),
SizedBox(height: 12),
Divider(
thickness: 1,
indent: 22,
endIndent: 22,
),
Divider(),
Expanded(
// TODO: move all tab info into _PeerTabbedPage
child: _PeerTabbedPage(
tabs: [
translate('Recent Sessions'),
@ -88,34 +86,6 @@ class _ConnectionPageState extends State<ConnectionPage> {
RecentPeerWidget(),
FavoritePeerWidget(),
DiscoveredPeerWidget(),
// AddressBookPeerWidget(),
// FutureBuilder<Widget>(
// future: getPeers(rType: RemoteType.recently),
// builder: (context, snapshot) {
// if (snapshot.hasData) {
// return snapshot.data!;
// } else {
// return Offstage();
// }
// }),
// FutureBuilder<Widget>(
// future: getPeers(rType: RemoteType.favorite),
// builder: (context, snapshot) {
// if (snapshot.hasData) {
// return snapshot.data!;
// } else {
// return Offstage();
// }
// }),
// FutureBuilder<Widget>(
// future: getPeers(rType: RemoteType.discovered),
// builder: (context, snapshot) {
// if (snapshot.hasData) {
// return snapshot.data!;
// } else {
// return Offstage();
// }
// }),
FutureBuilder<Widget>(
future: buildAddressBook(context),
builder: (context, snapshot) {
@ -126,7 +96,10 @@ class _ConnectionPageState extends State<ConnectionPage> {
}
}),
],
).marginSymmetric(horizontal: 6)),
)),
],
).marginSymmetric(horizontal: 22),
),
Divider(),
SizedBox(height: 50, child: Obx(() => buildStatus()))
.paddingSymmetric(horizontal: 12.0)
@ -193,7 +166,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
});
var w = Container(
width: 320 + 20 * 2,
padding: EdgeInsets.only(left: 20, right: 20, bottom: 22, top: 24),
padding: EdgeInsets.fromLTRB(20, 24, 20, 22),
decoration: BoxDecoration(
color: MyTheme.color(context).bg,
borderRadius: const BorderRadius.all(Radius.circular(13)),
@ -977,25 +950,22 @@ class _PeerTabbedPage extends StatefulWidget {
class _PeerTabbedPageState extends State<_PeerTabbedPage>
with SingleTickerProviderStateMixin {
late TabController _tabController;
late PageController _pageController = PageController();
RxInt _tabIndex = 0.obs;
@override
void initState() {
super.initState();
_tabController =
TabController(vsync: this, length: super.widget.tabs.length);
_tabController.addListener(_handleTabSelection);
}
// hard code for now
void _handleTabSelection() {
if (_tabController.indexIsChanging) {
void _handleTabSelection(int index) {
// reset search text
peerSearchText.value = "";
peerSearchTextController.clear();
_tabIndex.value = _tabController.index;
switch (_tabController.index) {
_tabIndex.value = index;
_pageController.jumpToPage(index);
switch (index) {
case 0:
bind.mainLoadRecentPeers();
break;
@ -1009,106 +979,150 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
break;
}
}
}
@override
void dispose() {
_tabController.dispose();
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// return DefaultTabController(
// length: 4,
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// _createTabBar(),
// _createTabBarView(),
// ],
// ));
return Column(
textBaseline: TextBaseline.ideographic,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
Container(
height: 28,
child: Row(
children: [
Expanded(child: _createTabBar(context)),
_createSearchBar(context),
_createPeerViewTypeSwitch(context),
],
),
),
_createTabBarView(),
],
);
}
Widget _createTabBar(BuildContext context) {
return TabBar(
isScrollable: true,
indicatorSize: TabBarIndicatorSize.label,
indicatorColor: Colors.transparent,
indicatorWeight: 0.1,
controller: _tabController,
labelPadding: EdgeInsets.zero,
padding: EdgeInsets.only(left: 16),
tabs: super.widget.tabs.asMap().entries.map((t) {
return Obx(() => Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
return ListView(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
children: super.widget.tabs.asMap().entries.map((t) {
return Obx(() => GestureDetector(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color:
_tabIndex.value == t.key ? MyTheme.color(context).bg : null,
color: _tabIndex.value == t.key
? MyTheme.color(context).bg
: null,
borderRadius: BorderRadius.circular(2),
),
child: Align(
alignment: Alignment.center,
child: Text(
t.value,
textAlign: TextAlign.center,
style: TextStyle(
height: 1,
fontSize: 14,
color: _tabIndex.value == t.key
? MyTheme.color(context).text
: MyTheme.color(context).lightText),
)));
),
)),
onTap: () => _handleTabSelection(t.key),
));
}).toList());
}
Widget _createTabBarView() {
return Expanded(
child: TabBarView(
controller: _tabController, children: super.widget.children)
.paddingSymmetric(horizontal: 12.0, vertical: 4.0));
child: PageView(
controller: _pageController, children: super.widget.children)
.marginSymmetric(vertical: 12));
}
_createSearchBar(BuildContext context) {
RxBool focused = false.obs;
FocusNode focusNode = FocusNode();
focusNode.addListener(() => focused.value = focusNode.hasFocus);
RxBool rowHover = false.obs;
RxBool clearHover = false.obs;
return Container(
width: 175,
height: 30,
margin: EdgeInsets.only(right: 16),
decoration: BoxDecoration(color: Colors.white),
child: Obx(
() => TextField(
width: 120,
height: 25,
margin: EdgeInsets.only(right: 13),
decoration: BoxDecoration(color: MyTheme.color(context).bg),
child: Obx(() => Row(
children: [
Expanded(
child: MouseRegion(
onEnter: (_) => rowHover.value = true,
onExit: (_) => rowHover.value = false,
child: Row(
children: [
Icon(
IconFont.search,
size: 16,
color: MyTheme.color(context).placeholder,
).marginSymmetric(horizontal: 4),
Expanded(
child: TextField(
controller: peerSearchTextController,
onChanged: (searchText) {
peerSearchText.value = searchText;
},
focusNode: focusNode,
textAlign: TextAlign.start,
maxLines: 1,
cursorColor: MyTheme.color(context).lightText,
cursorHeight: 18,
cursorWidth: 1,
style: TextStyle(fontSize: 14),
decoration: InputDecoration(
prefixIcon: Icon(
Icons.search,
size: 20,
),
contentPadding: EdgeInsets.zero,
hintText: translate("Search ID"),
hintStyle: TextStyle(fontSize: 14),
border: OutlineInputBorder(),
contentPadding: EdgeInsets.symmetric(vertical: 6),
hintText:
focused.value ? null : translate("Search ID"),
hintStyle: TextStyle(
fontSize: 14,
color: MyTheme.color(context).placeholder),
border: InputBorder.none,
isDense: true,
),
),
),
],
),
),
),
Offstage(
offstage: !(peerSearchText.value.isNotEmpty &&
(rowHover.value || clearHover.value)),
child: InkWell(
onHover: (value) => clearHover.value = value,
child: Icon(
IconFont.round_close,
size: 16,
color: clearHover.value
? MyTheme.color(context).text
: MyTheme.color(context).placeholder,
).marginSymmetric(horizontal: 4),
onTap: () {
peerSearchTextController.clear();
peerSearchText.value = "";
}),
)
],
)),
);
}
_createPeerViewTypeSwitch(BuildContext context) {
final activeDeco = BoxDecoration(color: Colors.white);
final activeDeco = BoxDecoration(color: MyTheme.color(context).bg);
return Row(
children: [
Obx(
@ -1122,8 +1136,10 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
},
child: Icon(
Icons.grid_view_rounded,
size: 20,
color: Colors.black54,
size: 18,
color: peerCardUiType.value == PeerUiType.grid
? MyTheme.color(context).text
: MyTheme.color(context).lightText,
)),
),
),
@ -1138,12 +1154,14 @@ class _PeerTabbedPageState extends State<_PeerTabbedPage>
},
child: Icon(
Icons.list,
size: 24,
color: Colors.black54,
size: 18,
color: peerCardUiType.value == PeerUiType.list
? MyTheme.color(context).text
: MyTheme.color(context).lightText,
)),
),
),
],
).paddingOnly(right: 16.0);
);
}
}

View File

@ -107,9 +107,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 15,
height: 25,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
translate("ID"),
@ -133,7 +134,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
readOnly: true,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.only(bottom: 8),
contentPadding: EdgeInsets.only(bottom: 18),
),
style: TextStyle(
fontSize: 22,
@ -239,19 +240,11 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
},
child: Obx(
() => Container(
decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(10),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: hover.value
() => CircleAvatar(
radius: 12,
backgroundColor: hover.value
? MyTheme.color(context).grayBg!
: MyTheme.color(context).bg!,
spreadRadius: 2)
],
),
child: Center(
child: Icon(
Icons.more_vert_outlined,
size: 20,
@ -261,7 +254,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
),
),
),
),
onHover: (value) => hover.value = value,
);
}

View File

@ -100,10 +100,10 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
offstage: super.widget._offstageFunc(peer),
child: Obx(
() => Container(
width: 225,
width: 220,
height: peerCardUiType.value == PeerUiType.grid
? 150
: 50,
? 140
: 42,
child: VisibilityDetector(
key: Key('${peer.id}'),
onVisibilityChanged: (info) {

View File

@ -36,29 +36,31 @@ class _PeerCard extends StatefulWidget {
class _PeerCardState extends State<_PeerCard>
with AutomaticKeepAliveClientMixin {
var _menuPos;
final double _cardRadis = 20;
final double _borderWidth = 2;
@override
Widget build(BuildContext context) {
super.build(context);
final peer = super.widget.peer;
var deco = Rx<BoxDecoration?>(BoxDecoration(
border: Border.all(color: Colors.transparent, width: 1.0),
border: Border.all(color: Colors.transparent, width: _borderWidth),
borderRadius: peerCardUiType.value == PeerUiType.grid
? BorderRadius.circular(20)
? BorderRadius.circular(_cardRadis)
: null));
return MouseRegion(
onEnter: (evt) {
deco.value = BoxDecoration(
border: Border.all(color: Colors.blue, width: 1.0),
border: Border.all(color: MyTheme.button, width: _borderWidth),
borderRadius: peerCardUiType.value == PeerUiType.grid
? BorderRadius.circular(20)
? BorderRadius.circular(_cardRadis)
: null);
},
onExit: (evt) {
deco.value = BoxDecoration(
border: Border.all(color: Colors.transparent, width: 1.0),
border: Border.all(color: Colors.transparent, width: _borderWidth),
borderRadius: peerCardUiType.value == PeerUiType.grid
? BorderRadius.circular(20)
? BorderRadius.circular(_cardRadis)
: null);
},
child: GestureDetector(
@ -71,25 +73,25 @@ class _PeerCardState extends State<_PeerCard>
Widget _buildPeerTile(
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
final greyStyle = TextStyle(fontSize: 12, color: Colors.grey);
final greyStyle =
TextStyle(fontSize: 12, color: MyTheme.color(context).lighterText);
RxBool iconHover = false.obs;
return Obx(
() => Container(
decoration: deco.value,
foregroundDecoration: deco.value,
child: Row(
mainAxisSize: MainAxisSize.max,
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),
child: _getPlatformImage('${peer.platform}', 30).paddingAll(6),
),
Expanded(
child: Container(
decoration: BoxDecoration(color: Colors.white),
decoration: BoxDecoration(color: MyTheme.color(context).bg),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -98,17 +100,17 @@ class _PeerCardState extends State<_PeerCard>
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(children: [
Text(
'${peer.id}',
style: TextStyle(fontWeight: FontWeight.w400),
),
Padding(
padding: EdgeInsets.fromLTRB(4, 4, 8, 4),
padding: EdgeInsets.fromLTRB(0, 4, 4, 4),
child: CircleAvatar(
radius: 5,
backgroundColor: peer.online
? Colors.green
: Colors.yellow)),
Text(
'${peer.id}',
style: TextStyle(fontWeight: FontWeight.w400),
),
]),
Align(
alignment: Alignment.centerLeft,
@ -122,6 +124,7 @@ class _PeerCardState extends State<_PeerCard>
: snapshot.data!;
return Tooltip(
message: name,
waitDuration: Duration(seconds: 1),
child: Text(
name,
style: greyStyle,
@ -145,7 +148,19 @@ class _PeerCardState extends State<_PeerCard>
),
),
InkWell(
child: Icon(Icons.more_vert),
child: CircleAvatar(
radius: 12,
backgroundColor: iconHover.value
? MyTheme.color(context).grayBg!
: MyTheme.color(context).bg!,
child: Icon(
Icons.more_vert,
size: 18,
color: iconHover.value
? MyTheme.color(context).text
: MyTheme.color(context).lightText,
),
),
onTapDown: (e) {
final x = e.globalPosition.dx;
final y = e.globalPosition.dy;
@ -153,9 +168,11 @@ class _PeerCardState extends State<_PeerCard>
},
onTap: () {
_showPeerMenu(context, peer.id);
}),
},
onHover: (value) => iconHover.value = value,
),
],
).paddingSymmetric(horizontal: 8.0),
).paddingSymmetric(horizontal: 4.0),
),
)
],
@ -166,24 +183,23 @@ class _PeerCardState extends State<_PeerCard>
Widget _buildPeerCard(
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
RxBool iconHover = false.obs;
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
color: Colors.transparent,
elevation: 0,
margin: EdgeInsets.zero,
child: Obx(
() => Container(
decoration: deco.value,
foregroundDecoration: deco.value,
child: ClipRRect(
borderRadius: BorderRadius.circular(_cardRadis - _borderWidth),
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(
@ -192,7 +208,8 @@ class _PeerCardState extends State<_PeerCard>
children: [
Container(
padding: const EdgeInsets.all(6),
child: _getPlatformImage('${peer.platform}'),
child:
_getPlatformImage('${peer.platform}', 60),
),
Row(
children: [
@ -207,6 +224,7 @@ class _PeerCardState extends State<_PeerCard>
: snapshot.data!;
return Tooltip(
message: name,
waitDuration: Duration(seconds: 1),
child: Text(
name,
style: TextStyle(
@ -240,7 +258,9 @@ class _PeerCardState extends State<_PeerCard>
),
),
),
Row(
Container(
color: MyTheme.color(context).bg,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(children: [
@ -248,12 +268,22 @@ class _PeerCardState extends State<_PeerCard>
padding: EdgeInsets.fromLTRB(0, 4, 8, 4),
child: CircleAvatar(
radius: 5,
backgroundColor:
peer.online ? Colors.green : Colors.yellow)),
backgroundColor: peer.online
? Colors.green
: Colors.yellow)),
Text('${peer.id}')
]),
InkWell(
child: Icon(Icons.more_vert),
child: CircleAvatar(
radius: 12,
backgroundColor: iconHover.value
? MyTheme.color(context).grayBg!
: MyTheme.color(context).bg!,
child: Icon(Icons.more_vert,
size: 18,
color: iconHover.value
? MyTheme.color(context).text
: MyTheme.color(context).lightText)),
onTapDown: (e) {
final x = e.globalPosition.dx;
final y = e.globalPosition.dy;
@ -261,13 +291,16 @@ class _PeerCardState extends State<_PeerCard>
},
onTap: () {
_showPeerMenu(context, peer.id);
}),
},
onHover: (value) => iconHover.value = value),
],
).paddingSymmetric(vertical: 8.0, horizontal: 12.0)
).paddingSymmetric(vertical: 8.0, horizontal: 12.0),
)
],
),
),
),
),
);
}
@ -365,12 +398,12 @@ class _PeerCardState extends State<_PeerCard>
}
/// Get the image for the current [platform].
Widget _getPlatformImage(String platform) {
Widget _getPlatformImage(String platform, double size) {
platform = platform.toLowerCase();
if (platform == 'mac os')
platform = 'mac';
else if (platform != 'linux' && platform != 'android') platform = 'win';
return Image.asset('assets/$platform.png', height: 50);
return Image.asset('assets/$platform.png', height: size, width: size);
}
void _abEditTag(String id) {

View File

@ -514,8 +514,10 @@ class ActionIcon extends StatelessWidget {
RxBool hover = false.obs;
return Obx(() => Tooltip(
message: translate(message),
waitDuration: Duration(seconds: 1),
child: InkWell(
hoverColor: is_close ? Colors.red : theme.hoverColor,
hoverColor:
is_close ? Color.fromARGB(255, 196, 43, 28) : theme.hoverColor,
onHover: (value) => hover.value = value,
child: Container(
height: _kTabBarHeight - 1,

View File

@ -107,9 +107,14 @@ flutter:
- family: GestureIcons
fonts:
- asset: assets/gestures.ttf
- family: IconFont
- family: Tabbar
fonts:
- asset: assets/tabbar.ttf
- family: PeerSearchbar
fonts:
- asset: assets/peer_searchbar.ttf
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.