Update app icon

Fix desktop backup error
This commit is contained in:
chen08209
2024-07-21 21:51:56 +08:00
parent 721dd20251
commit 530817b268
55 changed files with 346 additions and 227 deletions

View File

@@ -101,9 +101,9 @@ class Other {
String getTrayIconPath() {
if (Platform.isWindows) {
return "assets/images/app_icon.ico";
return "assets/images/icon.ico";
} else {
return "assets/images/launch_icon.png";
return "assets/images/icon_monochrome.png";
}
}

View File

@@ -1,16 +1,29 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
@immutable
class Contributor {
final String avatar;
final String name;
final String link;
const Contributor({
required this.avatar,
required this.name,
required this.link,
});
}
class AboutFragment extends StatelessWidget {
const AboutFragment({super.key});
_checkUpdate(BuildContext context) async {
final commonScaffoldState = context.commonScaffoldState;
if (commonScaffoldState?.mounted != true) return;
final data =
await commonScaffoldState?.loadingRun<Map<String, dynamic>?>(
final data = await commonScaffoldState?.loadingRun<Map<String, dynamic>?>(
request.checkForUpdate,
title: appLocalizations.checkUpdate,
);
@@ -20,84 +33,40 @@ class AboutFragment extends StatelessWidget {
);
}
@override
Widget build(BuildContext context) {
return ListView(
padding: kMaterialListPadding.copyWith(
top: 16,
bottom: 16,
),
children: [
ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Image.asset(
'assets/images/launch_icon.png',
width: 100,
height: 100,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
appName,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
globalState.packageInfo.version,
style: Theme.of(context).textTheme.labelLarge,
)
],
)
],
),
const SizedBox(
height: 24,
),
Text(
appLocalizations.desc,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
const SizedBox(
height: 12,
),
ListTile(
List<Widget> _buildMoreSection(BuildContext context) {
return generateSection(
separated: false,
title: appLocalizations.more,
items: [
ListItem(
title: Text(appLocalizations.checkUpdate),
onTap: () {
_checkUpdate(context);
},
),
ListTile(
ListItem(
title: const Text("Telegram"),
onTap: () {
launchUrl(
Uri.parse("https://t.me/+G-veVtwBOl4wODc1"),
globalState.openUrl(
"https://t.me/+G-veVtwBOl4wODc1",
);
},
trailing: const Icon(Icons.launch),
),
ListTile(
ListItem(
title: Text(appLocalizations.project),
onTap: () {
launchUrl(
Uri.parse("https://github.com/$repository"),
globalState.openUrl(
"https://github.com/$repository",
);
},
trailing: const Icon(Icons.launch),
),
ListTile(
ListItem(
title: Text(appLocalizations.core),
onTap: () {
launchUrl(
Uri.parse("https://github.com/chen08209/Clash.Meta/tree/FlClash"),
globalState.openUrl(
"https://github.com/chen08209/Clash.Meta/tree/FlClash",
);
},
trailing: const Icon(Icons.launch),
@@ -105,4 +74,139 @@ class AboutFragment extends StatelessWidget {
],
);
}
List<Widget> _buildContributorsSection() {
const contributors = [
Contributor(
avatar: "assets/images/avatars/june2.jpg",
name: "June2",
link: "https://t.me/Jibadong",
),
Contributor(
avatar: "assets/images/avatars/arue.jpg",
name: "Arue",
link: "https://t.me/xrcm6868",
),
];
return generateSection(
separated: false,
title: appLocalizations.contributors,
items: [
ListItem(
title: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Wrap(
spacing: 24,
children: [
for (final contributor in contributors)
Avatar(
contributor: contributor,
),
],
),
),
)
],
);
}
@override
Widget build(BuildContext context) {
final items = [
ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Image.asset(
'assets/images/icon.png',
width: 64,
height: 64,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
appName,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
globalState.packageInfo.version,
style: Theme.of(context).textTheme.labelLarge,
)
],
)
],
),
const SizedBox(
height: 24,
),
Text(
appLocalizations.desc,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
const SizedBox(
height: 12,
),
..._buildContributorsSection(),
..._buildMoreSection(context),
];
return Padding(
padding: kMaterialListPadding.copyWith(
top: 16,
bottom: 16,
),
child: generateListView(items),
);
}
}
class Avatar extends StatelessWidget {
final Contributor contributor;
const Avatar({
super.key,
required this.contributor,
});
@override
Widget build(BuildContext context) {
return InkWell(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
hoverColor: Colors.transparent,
child: Column(
children: [
SizedBox(
width: 36,
height: 36,
child: CircleAvatar(
foregroundImage: AssetImage(
contributor.avatar,
),
),
),
const SizedBox(
height: 4,
),
Text(
contributor.name,
style: context.textTheme.bodySmall,
)
],
),
onTap: () {
globalState.openUrl(contributor.link);
},
);
}
}

View File

@@ -113,8 +113,7 @@ class _BackupAndRecoveryState extends State<BackupAndRecovery> {
return Center(
child: FadeBox(
key: const Key("fade_box_1"),
child: snapshot.connectionState ==
ConnectionState.waiting
child: snapshot.connectionState == ConnectionState.waiting
? const SizedBox(
width: 12,
height: 12,
@@ -159,12 +158,12 @@ class _BackupAndRecoveryState extends State<BackupAndRecovery> {
ListHeader(
title: appLocalizations.backupAndRecovery),
ListItem(
onTab: _backup,
onTap: _backup,
title: Text(appLocalizations.backup),
subtitle: Text(appLocalizations.backupDesc),
),
ListItem(
onTab: _handleRecovery,
onTap: _handleRecovery,
title: Text(appLocalizations.recovery),
subtitle: Text(appLocalizations.recoveryDesc),
),
@@ -341,13 +340,13 @@ class _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {
child: Wrap(
children: [
ListItem(
onTab: () {
onTap: () {
_handleOnTab(RecoveryOption.onlyProfiles);
},
title: Text(appLocalizations.recoveryProfiles),
),
ListItem(
onTab: () {
onTap: () {
_handleOnTab(RecoveryOption.all);
},
title: Text(appLocalizations.recoveryAll),

View File

@@ -210,7 +210,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
leading: const Icon(Icons.info_outline),
title: Text(appLocalizations.logLevel),
subtitle: Text(value.name),
onTab: () {
onTap: () {
_showLogLevelDialog(value);
},
);
@@ -223,7 +223,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
leading: const Icon(Icons.computer_outlined),
title: const Text("UA"),
subtitle: Text(value ?? appLocalizations.defaultText),
onTab: () {
onTap: () {
_showUaDialog(value);
},
);
@@ -236,7 +236,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
leading: const Icon(Icons.timeline),
title: Text(appLocalizations.testUrl),
subtitle: Text(value),
onTab: () {
onTap: () {
_modifyTestUrl(value);
},
);
@@ -246,7 +246,7 @@ class _ConfigFragmentState extends State<ConfigFragment> {
selector: (_, clashConfig) => clashConfig.mixedPort,
builder: (_, mixedPort, __) {
return ListItem(
onTab: () {
onTap: () {
_modifyMixedPort(mixedPort);
},
leading: const Icon(Icons.adjust_outlined),

View File

@@ -48,19 +48,19 @@ class AddProfile extends StatelessWidget {
leading: const Icon(Icons.qr_code),
title: Text(appLocalizations.qrcode),
subtitle: Text(appLocalizations.qrcodeDesc),
onTab: _toScan,
onTap: _toScan,
),
ListItem(
leading: const Icon(Icons.upload_file),
title: Text(appLocalizations.file),
subtitle: Text(appLocalizations.fileDesc),
onTab: _handleAddProfileFormFile,
onTap: _handleAddProfileFormFile,
),
ListItem(
leading: const Icon(Icons.cloud_download),
title: Text(appLocalizations.url),
subtitle: Text(appLocalizations.urlDesc),
onTab: _toAdd,
onTap: _toAdd,
)
],
);

View File

@@ -211,5 +211,8 @@
"sort": "Sort",
"columns": "Columns",
"proxiesSetting": "Proxies setting",
"proxyGroup": "Proxy group"
"proxyGroup": "Proxy group",
"go": "Go",
"externalLink": "External link",
"contributors": "Contributors"
}

View File

@@ -211,5 +211,8 @@
"sort": "排序",
"columns": "列数",
"proxiesSetting": "代理设置",
"proxyGroup": "代理组"
"proxyGroup": "代理组",
"go": "前往",
"externalLink": "外部链接",
"contributors": "贡献者"
}

View File

@@ -97,6 +97,7 @@ class MessageLookup extends MessageLookupByLibrary {
"connectionsDesc": MessageLookupByLibrary.simpleMessage(
"View current connections data"),
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"contributors": MessageLookupByLibrary.simpleMessage("Contributors"),
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
"core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
@@ -135,6 +136,7 @@ class MessageLookup extends MessageLookupByLibrary {
MessageLookupByLibrary.simpleMessage("ExternalController"),
"externalControllerDesc": MessageLookupByLibrary.simpleMessage(
"Once enabled, the Clash kernel can be controlled on port 9090"),
"externalLink": MessageLookupByLibrary.simpleMessage("External link"),
"externalResources":
MessageLookupByLibrary.simpleMessage("External resources"),
"file": MessageLookupByLibrary.simpleMessage("File"),
@@ -153,6 +155,7 @@ class MessageLookup extends MessageLookupByLibrary {
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
"Enabling will use the Geo low memory loader"),
"global": MessageLookupByLibrary.simpleMessage("Global"),
"go": MessageLookupByLibrary.simpleMessage("Go"),
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
"hours": MessageLookupByLibrary.simpleMessage("Hours"),
"importFromURL":

View File

@@ -79,6 +79,7 @@ class MessageLookup extends MessageLookupByLibrary {
"connections": MessageLookupByLibrary.simpleMessage("连接"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"contributors": MessageLookupByLibrary.simpleMessage("贡献者"),
"copy": MessageLookupByLibrary.simpleMessage("复制"),
"core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
@@ -111,6 +112,7 @@ class MessageLookup extends MessageLookupByLibrary {
"externalController": MessageLookupByLibrary.simpleMessage("外部控制器"),
"externalControllerDesc":
MessageLookupByLibrary.simpleMessage("开启后将可以通过9090端口控制Clash内核"),
"externalLink": MessageLookupByLibrary.simpleMessage("外部链接"),
"externalResources": MessageLookupByLibrary.simpleMessage("外部资源"),
"file": MessageLookupByLibrary.simpleMessage("文件"),
"fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"),
@@ -125,6 +127,7 @@ class MessageLookup extends MessageLookupByLibrary {
"geodataLoaderDesc":
MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
"global": MessageLookupByLibrary.simpleMessage("全局"),
"go": MessageLookupByLibrary.simpleMessage("前往"),
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"hours": MessageLookupByLibrary.simpleMessage("小时"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),

View File

@@ -2179,6 +2179,36 @@ class AppLocalizations {
args: [],
);
}
/// `Go`
String get go {
return Intl.message(
'Go',
name: 'go',
desc: '',
args: [],
);
}
/// `External link`
String get externalLink {
return Intl.message(
'External link',
name: 'externalLink',
desc: '',
args: [],
);
}
/// `Contributors`
String get contributors {
return Intl.message(
'Contributors',
name: 'contributors',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -13,6 +13,20 @@ typedef OnSelected = void Function(int index);
class HomePage extends StatelessWidget {
const HomePage({super.key});
_navigationBarContainer({
required BuildContext context,
required Widget child,
}) {
// if (!system.isDesktop) return child;
return Container(
padding: const EdgeInsets.all(16).copyWith(
right: 0,
),
color: context.colorScheme.surface,
child: child,
);
}
_getNavigationBar({
required BuildContext context,
required ViewMode viewMode,
@@ -34,57 +48,49 @@ class HomePage extends StatelessWidget {
);
}
final extended = viewMode == ViewMode.desktop;
// return NavigationRail(
// backgroundColor: context.colorScheme.surfaceContainer,
// groupAlignment: -0.8,
// destinations: navigationItems
// .map(
// (e) => NavigationRailDestination(
// icon: e.icon,
// label: Text(
// Intl.message(e.label),
// ),
// ),
// )
// .toList(),
// onDestinationSelected: globalState.appController.toPage,
// extended: extended,
// minExtendedWidth: 172,
// selectedIndex: currentIndex,
// labelType: extended
// ? NavigationRailLabelType.none
// : NavigationRailLabelType.selected,
// );
return Container(
padding: const EdgeInsets.all(16).copyWith(
right: 0,
),
color: context.colorScheme.surface,
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: NavigationRail(
backgroundColor: context.colorScheme.surfaceContainer,
groupAlignment: -0.8,
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(
Intl.message(e.label),
),
return _navigationBarContainer(
context: context,
child: NavigationRail(
groupAlignment: -0.8,
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(
Intl.message(e.label),
),
)
.toList(),
onDestinationSelected: globalState.appController.toPage,
extended: extended,
minExtendedWidth: 172,
selectedIndex: currentIndex,
labelType: extended
? NavigationRailLabelType.none
: NavigationRailLabelType.selected,
),
),
)
.toList(),
onDestinationSelected: globalState.appController.toPage,
extended: extended,
minExtendedWidth: 172,
selectedIndex: currentIndex,
labelType: extended
? NavigationRailLabelType.none
: NavigationRailLabelType.selected,
),
);
return NavigationRail(
groupAlignment: -0.95,
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(
Intl.message(e.label),
),
),
)
.toList(),
onDestinationSelected: globalState.appController.toPage,
extended: extended,
minExtendedWidth: 172,
selectedIndex: currentIndex,
labelType: extended
? NavigationRailLabelType.none
: NavigationRailLabelType.selected,
);
}
@override
@@ -110,7 +116,7 @@ class HomePage extends StatelessWidget {
final navigationItems = state.navigationItems;
final currentLabel = state.currentLabel;
final index = navigationItems.lastIndexWhere(
(element) => element.label == currentLabel,
(element) => element.label == currentLabel,
);
final currentIndex = index == -1 ? 0 : index;
final navigationBar = _getNavigationBar(
@@ -120,9 +126,9 @@ class HomePage extends StatelessWidget {
currentIndex: currentIndex,
);
final bottomNavigationBar =
viewMode == ViewMode.mobile ? navigationBar : null;
viewMode == ViewMode.mobile ? navigationBar : null;
final sideNavigationBar =
viewMode != ViewMode.mobile ? navigationBar : null;
viewMode != ViewMode.mobile ? navigationBar : null;
return CommonScaffold(
key: globalState.homeScaffoldKey,
title: Intl.message(

View File

@@ -7,6 +7,7 @@ import 'package:fl_clash/plugins/proxy.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'controller.dart';
@@ -123,7 +124,7 @@ class GlobalState {
}) async {
appState.isInit = clashCore.isInit;
if (!appState.isInit) {
if(Platform.isAndroid){
if (Platform.isAndroid) {
clashCore.setProps(
Props(
accessControl: config.isAccessControl ? config.accessControl : null,
@@ -261,6 +262,17 @@ class GlobalState {
return null;
}
}
openUrl(String url) {
showMessage(
message: TextSpan(text: url),
title: appLocalizations.externalLink,
confirmText: appLocalizations.go,
onTab: () {
launchUrl(Uri.parse(url));
},
);
}
}
final globalState = GlobalState();

View File

@@ -78,7 +78,7 @@ class ListItem<T> extends StatelessWidget {
final Widget? trailing;
final Delegate delegate;
final double? horizontalTitleGap;
final void Function()? onTab;
final void Function()? onTap;
const ListItem({
super.key,
@@ -89,7 +89,7 @@ class ListItem<T> extends StatelessWidget {
this.trailing,
this.horizontalTitleGap,
this.prue,
this.onTab,
this.onTap,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : delegate = const Delegate();
@@ -104,7 +104,7 @@ class ListItem<T> extends StatelessWidget {
this.horizontalTitleGap,
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTab = null;
}) : onTap = null;
const ListItem.next({
super.key,
@@ -117,7 +117,7 @@ class ListItem<T> extends StatelessWidget {
this.horizontalTitleGap,
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTab = null;
}) : onTap = null;
const ListItem.checkbox({
super.key,
@@ -130,7 +130,7 @@ class ListItem<T> extends StatelessWidget {
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : trailing = null,
onTab = null;
onTap = null;
const ListItem.switchItem({
super.key,
@@ -143,7 +143,7 @@ class ListItem<T> extends StatelessWidget {
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : trailing = null,
onTab = null;
onTap = null;
const ListItem.radio({
super.key,
@@ -156,10 +156,10 @@ class ListItem<T> extends StatelessWidget {
this.prue,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : leading = null,
onTab = null;
onTap = null;
_buildListTile({
void Function()? onTab,
void Function()? onTap,
Widget? trailing,
Widget? leading,
}) {
@@ -188,7 +188,7 @@ class ListItem<T> extends StatelessWidget {
}
return InkWell(
splashFactory: NoSplash.splashFactory,
onTap: onTab,
onTap: onTap,
child: Container(
padding: padding,
child: Row(
@@ -203,7 +203,7 @@ class ListItem<T> extends StatelessWidget {
title: title,
subtitle: subtitle,
titleAlignment: tileTitleAlignment,
onTap: onTab,
onTap: onTap,
trailing: trailing ?? this.trailing,
contentPadding: padding,
);
@@ -230,7 +230,7 @@ class ListItem<T> extends StatelessWidget {
action();
}
return _buildListTile(onTab: openAction);
return _buildListTile(onTap: openAction);
},
openBuilder: (_, action) {
return CommonScaffold.open(
@@ -245,7 +245,7 @@ class ListItem<T> extends StatelessWidget {
if (delegate is NextDelegate) {
final nextDelegate = delegate as NextDelegate;
return _buildListTile(
onTab: () {
onTap: () {
final isMobile =
globalState.appController.appState.viewMode == ViewMode.mobile;
if (!isMobile) {
@@ -272,7 +272,7 @@ class ListItem<T> extends StatelessWidget {
if (delegate is CheckboxDelegate) {
final checkboxDelegate = delegate as CheckboxDelegate;
return _buildListTile(
onTab: () {
onTap: () {
if (checkboxDelegate.onChanged != null) {
checkboxDelegate.onChanged!(!checkboxDelegate.value);
}
@@ -286,7 +286,7 @@ class ListItem<T> extends StatelessWidget {
if (delegate is SwitchDelegate) {
final switchDelegate = delegate as SwitchDelegate;
return _buildListTile(
onTab: () {
onTap: () {
if (switchDelegate.onChanged != null) {
switchDelegate.onChanged!(!switchDelegate.value);
}
@@ -300,7 +300,7 @@ class ListItem<T> extends StatelessWidget {
if (delegate is RadioDelegate) {
final radioDelegate = delegate as RadioDelegate<T>;
return _buildListTile(
onTab: () {
onTap: () {
if (radioDelegate.onChanged != null) {
radioDelegate.onChanged!(radioDelegate.value);
}
@@ -319,7 +319,7 @@ class ListItem<T> extends StatelessWidget {
}
return _buildListTile(
onTab: onTab,
onTap: onTap,
);
}
}

View File

@@ -28,39 +28,20 @@ showExtendPage(
builder: (_, viewWidth, __) {
final isMobile =
globalState.appController.appState.viewMode == ViewMode.mobile;
final commonScaffold = isMobile
? CommonScaffold(
actions: isMobile
? null
: [
const SizedBox(
height: kToolbarHeight,
width: kToolbarHeight,
child: CloseButton(),
),
],
title: title,
body: uniqueBody,
)
: Column(
children: [
AppBar(
automaticallyImplyLeading: false,
title: Text(title),
actions: const [
SizedBox(
height: kToolbarHeight,
width: kToolbarHeight,
child: CloseButton(),
)
],
),
Expanded(
flex: 1,
child: uniqueBody,
final commonScaffold = CommonScaffold(
automaticallyImplyLeading: isMobile ? true : false,
actions: isMobile
? null
: [
const SizedBox(
height: kToolbarHeight,
width: kToolbarHeight,
child: CloseButton(),
),
],
);
title: title,
body: uniqueBody,
);
return AnimatedContainer(
duration: kThemeAnimationDuration,
width: isMobile ? viewWidth : extendPageWidth ?? 300,

View File

@@ -594,6 +594,7 @@ Future<T?> showModalSideSheet<T>({
AppBar(
automaticallyImplyLeading: false,
title: Text(title),
centerTitle: false,
actions: const [
SizedBox(
height: kToolbarHeight,

View File

@@ -207,7 +207,6 @@ class _WindowHeaderState extends State<WindowHeader> {
children: [
Positioned(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onPanStart: (_) {
windowManager.startDragging();
},
@@ -215,7 +214,7 @@ class _WindowHeaderState extends State<WindowHeader> {
_updateMaximized();
},
child: Container(
color: context.colorScheme.surfaceContainerHigh,
color: context.colorScheme.surface,
alignment: Alignment.centerLeft,
height: kHeaderHeight,
),
@@ -247,10 +246,10 @@ class AppIcon extends StatelessWidget {
child: const Row(
children: [
SizedBox(
width: 28,
height: 28,
width: 24,
height: 24,
child: CircleAvatar(
foregroundImage: AssetImage("assets/images/launch_icon.png"),
foregroundImage: AssetImage("assets/images/icon.png"),
backgroundColor: Colors.transparent,
),
),