Updater

This commit is contained in:
LittleSheep 2025-02-23 15:41:03 +08:00
parent 997934f680
commit e8bc7261f3
11 changed files with 219 additions and 55 deletions

View File

@ -718,5 +718,8 @@
"stickersNew": "New Sticker", "stickersNew": "New Sticker",
"stickersNewDescription": "Create a new sticker belongs to this pack.", "stickersNewDescription": "Create a new sticker belongs to this pack.",
"stickersPackNew": "New Sticker Pack", "stickersPackNew": "New Sticker Pack",
"trayMenuShow": "Show" "trayMenuShow": "Show",
"update": "Update",
"forceUpdate": "Force Update",
"forceUpdateDescription": "Force to show the application update popup, even the new version is not available."
} }

View File

@ -716,5 +716,8 @@
"stickersNew": "新建贴图", "stickersNew": "新建贴图",
"stickersNewDescription": "创建一个新的贴图。", "stickersNewDescription": "创建一个新的贴图。",
"stickersPackNew": "新建贴图包", "stickersPackNew": "新建贴图包",
"trayMenuShow": "显示" "trayMenuShow": "显示",
"update": "更新",
"forceUpdate": "强制更新",
"forceUpdateDescription": "强制更新应用程序,即使有更新的版本可能不可用。"
} }

View File

@ -715,5 +715,9 @@
"fieldStickerAttachment": "附件", "fieldStickerAttachment": "附件",
"stickersNew": "新建貼圖", "stickersNew": "新建貼圖",
"stickersNewDescription": "創建一個新的貼圖。", "stickersNewDescription": "創建一個新的貼圖。",
"stickersPackNew": "新建貼圖包" "stickersPackNew": "新建貼圖包",
"trayMenuShow": "顯示",
"update": "更新",
"forceUpdate": "強制更新",
"forceUpdateDescription": "強制更新應用程序,即使有更新的版本可能不可用。"
} }

View File

@ -715,5 +715,9 @@
"fieldStickerAttachment": "附件", "fieldStickerAttachment": "附件",
"stickersNew": "新建貼圖", "stickersNew": "新建貼圖",
"stickersNewDescription": "創建一個新的貼圖。", "stickersNewDescription": "創建一個新的貼圖。",
"stickersPackNew": "新建貼圖包" "stickersPackNew": "新建貼圖包",
"trayMenuShow": "顯示",
"update": "更新",
"forceUpdate": "強制更新",
"forceUpdateDescription": "強制更新應用程序,即使有更新的版本可能不可用。"
} }

View File

@ -179,7 +179,7 @@ PODS:
- in_app_review (2.0.0): - in_app_review (2.0.0):
- Flutter - Flutter
- Kingfisher (8.2.0) - Kingfisher (8.2.0)
- livekit_client (2.3.6): - livekit_client (2.4.0):
- Flutter - Flutter
- flutter_webrtc - flutter_webrtc
- WebRTC-SDK (= 125.6422.06) - WebRTC-SDK (= 125.6422.06)
@ -426,7 +426,7 @@ SPEC CHECKSUMS:
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
in_app_review: a31b5257259646ea78e0e35fc914979b0031d011 in_app_review: a31b5257259646ea78e0e35fc914979b0031d011
Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d Kingfisher: 323e5c4ec7983aaace12af655a7b51a7f88a599d
livekit_client: 148b2cf67a09aaf475ba8e5bf1667fe10dc35f81 livekit_client: 9819ebc8be8ef00ed0fae7d806bf8938ec689573
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e

View File

@ -254,10 +254,9 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
receiveTimeout: const Duration(seconds: 60), receiveTimeout: const Duration(seconds: 60),
), ),
).get( ).get(
'https://git.solsynth.dev/api/v1/repos/HyperNet/Surface/tags?page=1&limit=1', 'https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest',
); );
final remoteVersionString = final remoteVersionString = resp.data?['tag_name'] ?? '0.0.0+0';
(resp.data as List).firstOrNull?['name'] ?? '0.0.0+0';
final remoteVersion = Version.parse(remoteVersionString.split('+').first); final remoteVersion = Version.parse(remoteVersionString.split('+').first);
final localVersion = Version.parse(localVersionString.split('+').first); final localVersion = Version.parse(localVersionString.split('+').first);
final remoteBuildNumber = final remoteBuildNumber =
@ -269,10 +268,12 @@ class _AppSplashScreenState extends State<_AppSplashScreen> with TrayListener {
remoteBuildNumber > localBuildNumber) && remoteBuildNumber > localBuildNumber) &&
mounted) { mounted) {
final config = context.read<ConfigProvider>(); final config = context.read<ConfigProvider>();
config.setUpdate(remoteVersionString); config.setUpdate(
remoteVersionString, resp.data?['body'] ?? 'No changelog');
log("[Update] Update available: $remoteVersionString"); log("[Update] Update available: $remoteVersionString");
} }
} catch (e) { } catch (e) {
log('[Error] Unable to check update: $e');
if (mounted) context.showErrorDialog('Unable to check update: $e'); if (mounted) context.showErrorDialog('Unable to check update: $e');
} }
} }

View File

@ -58,7 +58,8 @@ class ConfigProvider extends ChangeNotifier {
: false; : false;
} }
if (newDrawerIsExpanded != drawerIsExpanded || newDrawerIsCollapsed != drawerIsCollapsed) { if (newDrawerIsExpanded != drawerIsExpanded ||
newDrawerIsCollapsed != drawerIsCollapsed) {
drawerIsExpanded = newDrawerIsExpanded; drawerIsExpanded = newDrawerIsExpanded;
drawerIsCollapsed = newDrawerIsCollapsed; drawerIsCollapsed = newDrawerIsCollapsed;
notifyListeners(); notifyListeners();
@ -66,7 +67,9 @@ class ConfigProvider extends ChangeNotifier {
} }
FilterQuality get imageQuality { FilterQuality get imageQuality {
return kImageQualityLevel.values.elementAtOrNull(prefs.getInt('app_image_quality') ?? 3) ?? FilterQuality.high; return kImageQualityLevel.values
.elementAtOrNull(prefs.getInt('app_image_quality') ?? 3) ??
FilterQuality.high;
} }
String get serverUrl { String get serverUrl {
@ -76,6 +79,7 @@ class ConfigProvider extends ChangeNotifier {
bool get realmCompactView { bool get realmCompactView {
return prefs.getBool(kAppRealmCompactView) ?? false; return prefs.getBool(kAppRealmCompactView) ?? false;
} }
set realmCompactView(bool value) { set realmCompactView(bool value) {
prefs.setBool(kAppRealmCompactView, value); prefs.setBool(kAppRealmCompactView, value);
} }
@ -86,9 +90,11 @@ class ConfigProvider extends ChangeNotifier {
} }
String? updatableVersion; String? updatableVersion;
String? updatableChangelog;
void setUpdate(String newVersion) { void setUpdate(String newVersion, String newChangelog) {
updatableVersion = newVersion; updatableVersion = newVersion;
updatableChangelog = newChangelog;
notifyListeners(); notifyListeners();
} }
} }

View File

@ -1,10 +1,8 @@
import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui'; import 'dart:ui';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_app_update/flutter_app_update.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:gap/gap.dart'; import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -29,6 +27,7 @@ import 'package:surface/widgets/app_bar_leading.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/post/post_item.dart'; import 'package:surface/widgets/post/post_item.dart';
import 'package:surface/widgets/updater.dart';
class HomeScreenDashEntry { class HomeScreenDashEntry {
final String name; final String name;
@ -83,14 +82,20 @@ class _HomeScreenState extends State<HomeScreen> {
body: LayoutBuilder( body: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
return Align( return Align(
alignment: constraints.maxWidth > 640 ? Alignment.center : Alignment.topCenter, alignment: constraints.maxWidth > 640
? Alignment.center
: Alignment.topCenter,
child: Container( child: Container(
constraints: const BoxConstraints(maxWidth: 640), constraints: const BoxConstraints(maxWidth: 640),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: constraints.maxWidth > 640 ? MainAxisAlignment.center : MainAxisAlignment.start, mainAxisAlignment: constraints.maxWidth > 640
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: [ children: [
_HomeDashUpdateWidget(padding: const EdgeInsets.only(bottom: 8, left: 8, right: 8)), _HomeDashUpdateWidget(
padding: const EdgeInsets.only(
bottom: 8, left: 8, right: 8)),
_HomeDashSpecialDayWidget().padding(horizontal: 8), _HomeDashSpecialDayWidget().padding(horizontal: 8),
StaggeredGrid.extent( StaggeredGrid.extent(
maxCrossAxisExtent: 280, maxCrossAxisExtent: 280,
@ -136,21 +141,15 @@ class _HomeDashUpdateWidget extends StatelessWidget {
leading: Icon(Symbols.update), leading: Icon(Symbols.update),
title: Text('updateAvailable').tr(), title: Text('updateAvailable').tr(),
subtitle: Text(config.updatableVersion!), subtitle: Text(config.updatableVersion!),
trailing: (kIsWeb || Platform.isWindows || Platform.isLinux) trailing: IconButton(
? null icon: const Icon(Symbols.arrow_right_alt),
: IconButton( onPressed: () {
icon: const Icon(Symbols.arrow_right_alt), showModalBottomSheet(
onPressed: () { context: context,
final model = UpdateModel( builder: (context) => VersionUpdatePopup(),
'https://files.solsynth.dev/d/production01/solian/app-arm64-v8a-release.apk', );
'solian-app-release-${config.updatableVersion!}.apk', },
'ic_launcher', ),
'https://apps.apple.com/us/app/solian/id6499032345',
);
AzhonAppUpdate.update(model);
context.showSnackbar('updateOngoing'.tr());
},
),
), ),
), ),
); );
@ -166,7 +165,8 @@ class _HomeDashSpecialDayWidget extends StatefulWidget {
const _HomeDashSpecialDayWidget(); const _HomeDashSpecialDayWidget();
@override @override
State<_HomeDashSpecialDayWidget> createState() => _HomeDashSpecialDayWidgetState(); State<_HomeDashSpecialDayWidget> createState() =>
_HomeDashSpecialDayWidgetState();
} }
class _HomeDashSpecialDayWidgetState extends State<_HomeDashSpecialDayWidget> { class _HomeDashSpecialDayWidgetState extends State<_HomeDashSpecialDayWidget> {
@ -208,7 +208,9 @@ class _HomeDashSpecialDayWidgetState extends State<_HomeDashSpecialDayWidget> {
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
child: ListTile( child: ListTile(
leading: Text(kSpecialDaysSymbol[name] ?? '🎉').fontSize(24), leading: Text(kSpecialDaysSymbol[name] ?? '🎉').fontSize(24),
title: Text('pending$name').tr(args: [RelativeTime(context).format(date).replaceFirst('in', '').trim()]), title: Text('pending$name').tr(args: [
RelativeTime(context).format(date).replaceFirst('in', '').trim()
]),
subtitle: Row( subtitle: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
@ -297,12 +299,19 @@ class _HomeDashTodayNewsState extends State<_HomeDashTodayNews> {
children: [ children: [
Text( Text(
_article!.title, _article!.title,
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontSize: 18), style: Theme.of(context)
maxLines: MediaQuery.of(context).size.width >= 640 ? 2 : 1, .textTheme
.titleMedium!
.copyWith(fontSize: 18),
maxLines:
MediaQuery.of(context).size.width >= 640 ? 2 : 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
Text( Text(
parse(_article!.description).children.map((e) => e.text.trim()).join(), parse(_article!.description)
.children
.map((e) => e.text.trim())
.join(),
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
@ -313,9 +322,13 @@ class _HomeDashTodayNewsState extends State<_HomeDashTodayNews> {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
spacing: 2, spacing: 2,
children: [ children: [
Text(DateFormat().format(date)).textStyle(Theme.of(context).textTheme.bodySmall!), Text(DateFormat().format(date)).textStyle(
Text(' · ').textStyle(Theme.of(context).textTheme.bodySmall!).bold(), Theme.of(context).textTheme.bodySmall!),
Text(RelativeTime(context).format(date)).textStyle(Theme.of(context).textTheme.bodySmall!), Text(' · ')
.textStyle(Theme.of(context).textTheme.bodySmall!)
.bold(),
Text(RelativeTime(context).format(date)).textStyle(
Theme.of(context).textTheme.bodySmall!),
], ],
).opacity(0.75); ).opacity(0.75);
}), }),
@ -386,15 +399,20 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
} }
Widget _buildDetailChunk(int index, bool positive) { Widget _buildDetailChunk(int index, bool positive) {
final prefix = positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint'; final prefix =
final mod = positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount; positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint';
final mod =
positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount;
final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod); final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
prefix.tr(args: ['$prefix$pos'.tr()]), prefix.tr(args: ['$prefix$pos'.tr()]),
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
), ),
Text( Text(
'$prefix${pos}Description', '$prefix${pos}Description',
@ -429,7 +447,10 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
else else
Text( Text(
'dailyCheckEverythingIsNegative', 'dailyCheckEverythingIsNegative',
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
).tr(), ).tr(),
const Gap(8), const Gap(8),
if (_todayRecord?.resultTier != 4) if (_todayRecord?.resultTier != 4)
@ -445,7 +466,10 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
else else
Text( Text(
'dailyCheckEverythingIsPositive', 'dailyCheckEverythingIsPositive',
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
).tr(), ).tr(),
], ],
), ),
@ -571,10 +595,12 @@ class _HomeDashNotificationWidget extends StatefulWidget {
const _HomeDashNotificationWidget(); const _HomeDashNotificationWidget();
@override @override
State<_HomeDashNotificationWidget> createState() => _HomeDashNotificationWidgetState(); State<_HomeDashNotificationWidget> createState() =>
_HomeDashNotificationWidgetState();
} }
class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget> { class _HomeDashNotificationWidgetState
extends State<_HomeDashNotificationWidget> {
int? _count; int? _count;
Future<void> _fetchNotificationCount() async { Future<void> _fetchNotificationCount() async {
@ -612,7 +638,9 @@ class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
).tr(), ).tr(),
Text( Text(
_count == null ? 'loading'.tr() : 'notificationUnreadCount'.plural(_count ?? 0), _count == null
? 'loading'.tr()
: 'notificationUnreadCount'.plural(_count ?? 0),
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
], ],
@ -643,10 +671,12 @@ class _HomeDashRecommendationPostWidget extends StatefulWidget {
const _HomeDashRecommendationPostWidget(); const _HomeDashRecommendationPostWidget();
@override @override
State<_HomeDashRecommendationPostWidget> createState() => _HomeDashRecommendationPostWidgetState(); State<_HomeDashRecommendationPostWidget> createState() =>
_HomeDashRecommendationPostWidgetState();
} }
class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendationPostWidget> { class _HomeDashRecommendationPostWidgetState
extends State<_HomeDashRecommendationPostWidget> {
bool _isBusy = false; bool _isBusy = false;
List<SnPost>? _posts; List<SnPost>? _posts;
@ -710,13 +740,15 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati
).tr(), ).tr(),
], ],
), ),
Text('${_currentPage + 1}/${_posts?.length ?? 0}', style: GoogleFonts.robotoMono()) Text('${_currentPage + 1}/${_posts?.length ?? 0}',
style: GoogleFonts.robotoMono())
], ],
).padding(horizontal: 18, top: 12, bottom: 8), ).padding(horizontal: 18, top: 12, bottom: 8),
Expanded( Expanded(
child: PageView.builder( child: PageView.builder(
controller: _pageController, controller: _pageController,
scrollBehavior: ScrollConfiguration.of(context).copyWith(dragDevices: { scrollBehavior:
ScrollConfiguration.of(context).copyWith(dragDevices: {
PointerDeviceKind.mouse, PointerDeviceKind.mouse,
PointerDeviceKind.touch, PointerDeviceKind.touch,
}), }),
@ -729,7 +761,8 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati
showMenu: false, showMenu: false,
).padding(bottom: 8), ).padding(bottom: 8),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('postDetail', pathParameters: { GoRouter.of(context)
.pushNamed('postDetail', pathParameters: {
'slug': _posts![index].id.toString(), 'slug': _posts![index].id.toString(),
}); });
}, },

View File

@ -24,6 +24,7 @@ import 'package:surface/providers/theme.dart';
import 'package:surface/theme.dart'; import 'package:surface/theme.dart';
import 'package:surface/widgets/dialog.dart'; import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/navigation/app_scaffold.dart'; import 'package:surface/widgets/navigation/app_scaffold.dart';
import 'package:surface/widgets/updater.dart';
const Map<String, Color> kColorSchemes = { const Map<String, Color> kColorSchemes = {
'colorSchemeIndigo': Colors.indigo, 'colorSchemeIndigo': Colors.indigo,
@ -604,6 +605,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
} }
}, },
), ),
ListTile(
title: Text('forceUpdate').tr(),
subtitle: Text('forceUpdateDescription').tr(),
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
leading: const Icon(Symbols.update),
trailing: const Icon(Symbols.chevron_right),
onTap: () async {
showModalBottomSheet(
context: context,
builder: (context) => VersionUpdatePopup(),
);
},
),
ListTile( ListTile(
title: Text('settingsMiscAbout').tr(), title: Text('settingsMiscAbout').tr(),
subtitle: Text('settingsMiscAboutDescription').tr(), subtitle: Text('settingsMiscAboutDescription').tr(),

96
lib/widgets/updater.dart Normal file
View File

@ -0,0 +1,96 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_update/azhon_app_update.dart';
import 'package:flutter_app_update/update_model.dart';
import 'package:gap/gap.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/config.dart';
import 'package:surface/widgets/dialog.dart';
import 'package:surface/widgets/markdown_content.dart';
import 'package:url_launcher/url_launcher_string.dart';
class VersionUpdatePopup extends StatelessWidget {
const VersionUpdatePopup({super.key});
void _update(BuildContext context) async {
if (kIsWeb) return;
final config = context.read<ConfigProvider>();
if (Platform.isAndroid) {
final model = UpdateModel(
'https://files.solsynth.dev/d/production01/solian/app-arm64-v8a-release.apk',
'solian-app-release-${config.updatableVersion!}.apk',
'ic_launcher',
'https://apps.apple.com/us/app/solian/id6499032345',
);
AzhonAppUpdate.update(model);
context.showSnackbar('updateOngoing'.tr());
return;
}
final resp = await Dio(
BaseOptions(
sendTimeout: const Duration(seconds: 60),
receiveTimeout: const Duration(seconds: 60),
),
).get(
'https://api.github.com/repos/Solsynth/HyperNet.Surface/releases/latest',
);
launchUrlString(resp.data?['html_url']);
}
@override
Widget build(BuildContext context) {
final config = context.watch<ConfigProvider>();
return Column(
children: [
Row(
children: [
const Icon(Icons.update),
const Gap(16),
Text('update')
.tr()
.textStyle(Theme.of(context).textTheme.titleLarge!),
],
).padding(horizontal: 20, top: 16, bottom: 12),
Row(
children: [
Expanded(
child: Text(
config.updatableVersion ?? 'unknown'.tr(),
style: GoogleFonts.robotoMono(),
),
),
ElevatedButton(
style: ButtonStyle(
visualDensity: const VisualDensity(
horizontal: -4,
vertical: -3,
),
),
onPressed: () => _update(context),
child: Text('update').tr(),
),
],
).padding(horizontal: 20),
const Divider(height: 1).padding(vertical: 8),
Expanded(
child: SingleChildScrollView(
child: MarkdownTextContent(
content: config.updatableChangelog ?? 'No changelog',
).padding(horizontal: 20),
),
)
],
);
}
}

View File

@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 2.3.2+71 version: 2.3.2+70
environment: environment:
sdk: ^3.5.4 sdk: ^3.5.4