mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-30 17:19:03 +08:00
opt peer card multi select
Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
parent
5028b8a93d
commit
3fd58bb69d
BIN
flutter/assets/checkbox.ttf
Normal file
BIN
flutter/assets/checkbox.ttf
Normal file
Binary file not shown.
@ -90,6 +90,7 @@ class IconFont {
|
|||||||
static const IconData roundClose = IconData(0xe6ed, fontFamily: _family2);
|
static const IconData roundClose = IconData(0xe6ed, fontFamily: _family2);
|
||||||
static const IconData addressBook =
|
static const IconData addressBook =
|
||||||
IconData(0xe602, fontFamily: "AddressBook");
|
IconData(0xe602, fontFamily: "AddressBook");
|
||||||
|
static const IconData checkbox = IconData(0xe7d6, fontFamily: "CheckBox");
|
||||||
}
|
}
|
||||||
|
|
||||||
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
class ColorThemeExtension extends ThemeExtension<ColorThemeExtension> {
|
||||||
|
@ -61,20 +61,19 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
final name =
|
final name =
|
||||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||||
final PeerTabModel peerTabModel = Provider.of(context);
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
final selected = peerTabModel.isPeerSelected(peer.id);
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: EdgeInsets.symmetric(horizontal: 2),
|
margin: EdgeInsets.symmetric(horizontal: 2),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (peerTabModel.multiSelectionMode) {
|
if (peerTabModel.multiSelectionMode) {
|
||||||
peerTabModel.togglePeerSelect(peer);
|
peerTabModel.select(peer);
|
||||||
} else {
|
} else {
|
||||||
if (!isWebDesktop) connect(context, peer.id);
|
if (!isWebDesktop) connect(context, peer.id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDoubleTap: isWebDesktop ? () => connect(context, peer.id) : null,
|
onDoubleTap: isWebDesktop ? () => connect(context, peer.id) : null,
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
peerTabModel.togglePeerSelect(peer);
|
peerTabModel.select(peer);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
padding: EdgeInsets.only(left: 12, top: 8, bottom: 8),
|
||||||
@ -103,23 +102,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
],
|
],
|
||||||
).paddingOnly(left: 8.0),
|
).paddingOnly(left: 8.0),
|
||||||
),
|
),
|
||||||
selected
|
checkBoxOrActionMoreMobile(peer),
|
||||||
? Padding(
|
|
||||||
padding: const EdgeInsets.all(12),
|
|
||||||
child: checkBox(),
|
|
||||||
)
|
|
||||||
: InkWell(
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.all(12),
|
|
||||||
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(peer.id);
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
@ -159,15 +142,8 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
onDoubleTap: peerTabModel.multiSelectionMode
|
onDoubleTap: peerTabModel.multiSelectionMode
|
||||||
? null
|
? null
|
||||||
: () => widget.connect(context, peer.id),
|
: () => widget.connect(context, peer.id),
|
||||||
onLongPress: () {
|
onTap: () => peerTabModel.select(peer),
|
||||||
peerTabModel.togglePeerSelect(peer);
|
onLongPress: () => peerTabModel.select(peer),
|
||||||
},
|
|
||||||
onSecondaryTapDown: (_) {
|
|
||||||
peerTabModel.togglePeerSelect(peer);
|
|
||||||
},
|
|
||||||
onTap: peerTabModel.multiSelectionMode
|
|
||||||
? () => peerTabModel.togglePeerSelect(peer)
|
|
||||||
: null,
|
|
||||||
child: Obx(() => peerCardUiType.value == PeerUiType.grid
|
child: Obx(() => peerCardUiType.value == PeerUiType.grid
|
||||||
? _buildPeerCard(context, peer, deco)
|
? _buildPeerCard(context, peer, deco)
|
||||||
: _buildPeerTile(context, peer, deco))),
|
: _buildPeerTile(context, peer, deco))),
|
||||||
@ -176,8 +152,6 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
|
|
||||||
Widget _buildPeerTile(
|
Widget _buildPeerTile(
|
||||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
final PeerTabModel peerTabModel = Provider.of(context);
|
|
||||||
final selected = peerTabModel.isPeerSelected(peer.id);
|
|
||||||
final name =
|
final name =
|
||||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||||
final greyStyle = TextStyle(
|
final greyStyle = TextStyle(
|
||||||
@ -237,7 +211,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
],
|
],
|
||||||
).marginOnly(top: 2),
|
).marginOnly(top: 2),
|
||||||
),
|
),
|
||||||
selected ? checkBox() : _actionMore(peer),
|
checkBoxOrActionMoreDesktop(peer),
|
||||||
],
|
],
|
||||||
).paddingOnly(left: 10.0, top: 3.0),
|
).paddingOnly(left: 10.0, top: 3.0),
|
||||||
),
|
),
|
||||||
@ -250,8 +224,6 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
|
|
||||||
Widget _buildPeerCard(
|
Widget _buildPeerCard(
|
||||||
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
BuildContext context, Peer peer, Rx<BoxDecoration?> deco) {
|
||||||
final PeerTabModel peerTabModel = Provider.of(context);
|
|
||||||
final selected = peerTabModel.isPeerSelected(peer.id);
|
|
||||||
final name =
|
final name =
|
||||||
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
'${peer.username}${peer.username.isNotEmpty && peer.hostname.isNotEmpty ? '@' : ''}${peer.hostname}';
|
||||||
return Card(
|
return Card(
|
||||||
@ -321,7 +293,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleSmall,
|
||||||
)),
|
)),
|
||||||
]).paddingSymmetric(vertical: 8)),
|
]).paddingSymmetric(vertical: 8)),
|
||||||
selected ? checkBox() : _actionMore(peer),
|
checkBoxOrActionMoreDesktop(peer),
|
||||||
],
|
],
|
||||||
).paddingSymmetric(horizontal: 12.0),
|
).paddingSymmetric(horizontal: 12.0),
|
||||||
)
|
)
|
||||||
@ -333,11 +305,57 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget checkBox() {
|
Widget checkBoxOrActionMoreMobile(Peer peer) {
|
||||||
return Icon(
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
Icons.check_box,
|
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||||
color: MyTheme.accent,
|
if (peerTabModel.multiSelectionMode) {
|
||||||
);
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
child: selected
|
||||||
|
? Icon(
|
||||||
|
Icons.check_box,
|
||||||
|
color: MyTheme.accent,
|
||||||
|
)
|
||||||
|
: Icon(Icons.check_box_outline_blank),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return InkWell(
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(12), 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(peer.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget checkBoxOrActionMoreDesktop(Peer peer) {
|
||||||
|
final PeerTabModel peerTabModel = Provider.of(context);
|
||||||
|
final selected = peerTabModel.isPeerSelected(peer.id);
|
||||||
|
if (peerTabModel.multiSelectionMode) {
|
||||||
|
final icon = selected
|
||||||
|
? Icon(
|
||||||
|
Icons.check_box,
|
||||||
|
color: MyTheme.accent,
|
||||||
|
)
|
||||||
|
: Icon(Icons.check_box_outline_blank);
|
||||||
|
bool last = peerTabModel.isShiftDown && peer.id == peerTabModel.lastId;
|
||||||
|
if (last) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: MyTheme.accent, width: 1)),
|
||||||
|
child: icon,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return _actionMore(peer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _actionMore(Peer peer) => Listener(
|
Widget _actionMore(Peer peer) => Listener(
|
||||||
|
@ -103,6 +103,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
Expanded(child: _createSwitchBar(context)),
|
Expanded(child: _createSwitchBar(context)),
|
||||||
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
const PeerSearchBar().marginOnly(right: isMobile ? 0 : 13),
|
||||||
_createRefresh(),
|
_createRefresh(),
|
||||||
|
_createMultiSelection(),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: !isDesktop,
|
offstage: !isDesktop,
|
||||||
child: _createPeerViewTypeSwitch(context)),
|
child: _createPeerViewTypeSwitch(context)),
|
||||||
@ -258,6 +259,24 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _createMultiSelection() {
|
||||||
|
final textColor = Theme.of(context).textTheme.titleLarge?.color;
|
||||||
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.all(4.0),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
model.setMultiSelectionMode(true);
|
||||||
|
},
|
||||||
|
child: Icon(
|
||||||
|
IconFont.checkbox,
|
||||||
|
size: 18,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget createMultiSelectionBar() {
|
Widget createMultiSelectionBar() {
|
||||||
final model = Provider.of<PeerTabModel>(context);
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
return Row(
|
return Row(
|
||||||
@ -308,7 +327,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
gFFI.peerTabModel.closeSelection();
|
gFFI.peerTabModel.setMultiSelectionMode(false);
|
||||||
showToast(translate('Successful'));
|
showToast(translate('Successful'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +353,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
await bind.mainStoreFav(favs: favs);
|
await bind.mainStoreFav(favs: favs);
|
||||||
gFFI.peerTabModel.closeSelection();
|
model.setMultiSelectionMode(false);
|
||||||
showToast(translate('Successful'));
|
showToast(translate('Successful'));
|
||||||
},
|
},
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
@ -355,7 +374,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
final peers = model.selectedPeers;
|
final peers = model.selectedPeers;
|
||||||
gFFI.abModel.addPeers(peers);
|
gFFI.abModel.addPeers(peers);
|
||||||
gFFI.abModel.pushAb();
|
gFFI.abModel.pushAb();
|
||||||
gFFI.peerTabModel.closeSelection();
|
model.setMultiSelectionMode(false);
|
||||||
showToast(translate('Successful'));
|
showToast(translate('Successful'));
|
||||||
},
|
},
|
||||||
child: Tooltip(
|
child: Tooltip(
|
||||||
@ -379,7 +398,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
gFFI.abModel.changeTagForPeers(
|
gFFI.abModel.changeTagForPeers(
|
||||||
peers.map((p) => p.id).toList(), selectedTags);
|
peers.map((p) => p.id).toList(), selectedTags);
|
||||||
gFFI.abModel.pushAb();
|
gFFI.abModel.pushAb();
|
||||||
gFFI.peerTabModel.closeSelection();
|
model.setMultiSelectionMode(false);
|
||||||
showToast(translate('Successful'));
|
showToast(translate('Successful'));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -416,7 +435,7 @@ class _PeerTabPageState extends State<PeerTabPage>
|
|||||||
final model = Provider.of<PeerTabModel>(context);
|
final model = Provider.of<PeerTabModel>(context);
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
model.closeSelection();
|
model.setMultiSelectionMode(false);
|
||||||
},
|
},
|
||||||
child:
|
child:
|
||||||
Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
|
Tooltip(message: translate('Close'), child: Icon(Icons.clear)))
|
||||||
|
@ -418,7 +418,7 @@ class _AppState extends State<App> {
|
|||||||
: (context, child) {
|
: (context, child) {
|
||||||
child = _keepScaleBuilder(context, child);
|
child = _keepScaleBuilder(context, child);
|
||||||
child = botToastBuilder(context, child);
|
child = botToastBuilder(context, child);
|
||||||
if (desktopType == DesktopType.main) {
|
if (isDesktop && desktopType == DesktopType.main) {
|
||||||
child = keyListenerBuilder(context, child);
|
child = keyListenerBuilder(context, child);
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
@ -465,9 +465,9 @@ Widget keyListenerBuilder(BuildContext context, Widget? child) {
|
|||||||
onKey: (RawKeyEvent event) {
|
onKey: (RawKeyEvent event) {
|
||||||
if (event.logicalKey == LogicalKeyboardKey.shiftLeft) {
|
if (event.logicalKey == LogicalKeyboardKey.shiftLeft) {
|
||||||
if (event is RawKeyDownEvent) {
|
if (event is RawKeyDownEvent) {
|
||||||
gFFI.peerTabModel.isShiftDown = true;
|
gFFI.peerTabModel.setShiftDown(true);
|
||||||
} else if (event is RawKeyUpEvent) {
|
} else if (event is RawKeyUpEvent) {
|
||||||
gFFI.peerTabModel.isShiftDown = false;
|
gFFI.peerTabModel.setShiftDown(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -39,11 +39,14 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
List<int> get indexs => List.generate(tabNames.length, (index) => index);
|
||||||
List<Peer> _selectedPeers = List.empty(growable: true);
|
List<Peer> _selectedPeers = List.empty(growable: true);
|
||||||
List<Peer> get selectedPeers => _selectedPeers;
|
List<Peer> get selectedPeers => _selectedPeers;
|
||||||
bool get multiSelectionMode => _selectedPeers.isNotEmpty;
|
bool _multiSelectionMode = false;
|
||||||
|
bool get multiSelectionMode => _multiSelectionMode;
|
||||||
List<Peer> _currentTabCachedPeers = List.empty(growable: true);
|
List<Peer> _currentTabCachedPeers = List.empty(growable: true);
|
||||||
List<Peer> get currentTabCachedPeers => _currentTabCachedPeers;
|
List<Peer> get currentTabCachedPeers => _currentTabCachedPeers;
|
||||||
bool isShiftDown = false;
|
bool _isShiftDown = false;
|
||||||
String? _shiftAnchorId;
|
bool get isShiftDown => _isShiftDown;
|
||||||
|
String _lastId = '';
|
||||||
|
String get lastId => _lastId;
|
||||||
|
|
||||||
PeerTabModel(this.parent) {
|
PeerTabModel(this.parent) {
|
||||||
// init currentTab
|
// init currentTab
|
||||||
@ -85,38 +88,39 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
return Icons.help;
|
return Icons.help;
|
||||||
}
|
}
|
||||||
|
|
||||||
togglePeerSelect(Peer peer) {
|
setMultiSelectionMode(bool mode) {
|
||||||
|
_multiSelectionMode = mode;
|
||||||
|
if (!mode) {
|
||||||
|
_selectedPeers.clear();
|
||||||
|
_lastId = '';
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
select(Peer peer) {
|
||||||
|
if (!_multiSelectionMode) {
|
||||||
|
// https://github.com/flutter/flutter/issues/101275#issuecomment-1604541700
|
||||||
|
// After onTap, the shift key should be pressed for a while when not in multiselection mode,
|
||||||
|
// because onTap is delayed when onDoubleTap is not null
|
||||||
|
if (isDesktop && !_isShiftDown) return;
|
||||||
|
_multiSelectionMode = true;
|
||||||
|
}
|
||||||
final cached = _currentTabCachedPeers.map((e) => e.id).toList();
|
final cached = _currentTabCachedPeers.map((e) => e.id).toList();
|
||||||
int thisIndex = cached.indexOf(peer.id);
|
int thisIndex = cached.indexOf(peer.id);
|
||||||
int closestIndex = -1;
|
int lastIndex = cached.indexOf(_lastId);
|
||||||
String? closestId;
|
if (_isShiftDown && thisIndex >= 0 && lastIndex >= 0) {
|
||||||
int smallestDiff = -1;
|
int start = min(thisIndex, lastIndex);
|
||||||
for (var i = 0; i < cached.length; i++) {
|
int end = max(thisIndex, lastIndex);
|
||||||
if (isPeerSelected(cached[i])) {
|
bool remove = isPeerSelected(peer.id);
|
||||||
int diff = (i - thisIndex).abs();
|
|
||||||
if (smallestDiff == -1 || diff < smallestDiff) {
|
|
||||||
closestIndex = i;
|
|
||||||
closestId = cached[i];
|
|
||||||
smallestDiff = diff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isShiftDown &&
|
|
||||||
thisIndex >= 0 &&
|
|
||||||
closestIndex >= 0 &&
|
|
||||||
closestId != null) {
|
|
||||||
int shiftAnchorIndex = cached.indexOf(_shiftAnchorId ?? '');
|
|
||||||
if (shiftAnchorIndex < 0) {
|
|
||||||
// use closest as shift anchor, rather than focused which we don't have
|
|
||||||
shiftAnchorIndex = closestIndex;
|
|
||||||
_shiftAnchorId = closestId;
|
|
||||||
}
|
|
||||||
int start = min(shiftAnchorIndex, thisIndex);
|
|
||||||
int end = max(shiftAnchorIndex, thisIndex);
|
|
||||||
_selectedPeers.clear();
|
|
||||||
for (var i = start; i <= end; i++) {
|
for (var i = start; i <= end; i++) {
|
||||||
if (!isPeerSelected(cached[i])) {
|
if (remove) {
|
||||||
_selectedPeers.add(_currentTabCachedPeers[i]);
|
if (isPeerSelected(cached[i])) {
|
||||||
|
_selectedPeers.removeWhere((p) => p.id == cached[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!isPeerSelected(cached[i])) {
|
||||||
|
_selectedPeers.add(_currentTabCachedPeers[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -125,14 +129,8 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
} else {
|
} else {
|
||||||
_selectedPeers.add(peer);
|
_selectedPeers.add(peer);
|
||||||
}
|
}
|
||||||
_shiftAnchorId = null;
|
|
||||||
}
|
}
|
||||||
notifyListeners();
|
_lastId = peer.id;
|
||||||
}
|
|
||||||
|
|
||||||
closeSelection() {
|
|
||||||
_selectedPeers.clear();
|
|
||||||
_shiftAnchorId = null;
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,4 +149,13 @@ class PeerTabModel with ChangeNotifier {
|
|||||||
bool isPeerSelected(String id) {
|
bool isPeerSelected(String id) {
|
||||||
return selectedPeers.firstWhereOrNull((p) => p.id == id) != null;
|
return selectedPeers.firstWhereOrNull((p) => p.id == id) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setShiftDown(bool v) {
|
||||||
|
if (_isShiftDown != v) {
|
||||||
|
_isShiftDown = v;
|
||||||
|
if (_multiSelectionMode) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,9 @@ flutter:
|
|||||||
- family: AddressBook
|
- family: AddressBook
|
||||||
fonts:
|
fonts:
|
||||||
- asset: assets/address_book.ttf
|
- asset: assets/address_book.ttf
|
||||||
|
- family: CheckBox
|
||||||
|
fonts:
|
||||||
|
- asset: assets/checkbox.ttf
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||||
|
Loading…
Reference in New Issue
Block a user