auto upgrade
This commit is contained in:
parent
d2b2265900
commit
ed8aa0d57d
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="pikapi"
|
android:label="pikapika"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true">
|
||||||
<!-- requestLegacyExternalStorage="true" api29 down -->
|
<!-- requestLegacyExternalStorage="true" api29 down -->
|
||||||
<activity
|
<activity
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
gomobile bind -target=android/arm,android/arm64,android/386 -o lib/Pikapi.aar ./
|
gomobile bind -target=android/arm,android/arm64,android/386 -o lib/Mobile.aar ./
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
gomobile bind -target=android/arm -o lib/Pikapi.aar ./
|
gomobile bind -target=android/arm -o lib/Mobile.aar ./
|
||||||
|
|
|
@ -99,12 +99,10 @@ Future<T?> chooseListDialog<T>(
|
||||||
{String? tips}) async {
|
{String? tips}) async {
|
||||||
List<Widget> widgets = [];
|
List<Widget> widgets = [];
|
||||||
if (tips != null) {
|
if (tips != null) {
|
||||||
widgets.add(
|
widgets.add(Container(
|
||||||
Container(
|
|
||||||
padding: EdgeInsets.fromLTRB(15, 5, 15, 15),
|
padding: EdgeInsets.fromLTRB(15, 5, 15, 15),
|
||||||
child: Text(tips),
|
child: Text(tips),
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
widgets.addAll(items.map((e) => SimpleDialogOption(
|
widgets.addAll(items.map((e) => SimpleDialogOption(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
|
@ -1,35 +1,155 @@
|
||||||
import 'dart:async' show Future;
|
import 'dart:async' show Future;
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:event/event.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
|
import 'package:pikapika/basic/Common.dart';
|
||||||
|
|
||||||
import '../Method.dart';
|
import '../Method.dart';
|
||||||
|
|
||||||
const _versionUrl =
|
const _versionUrl =
|
||||||
"https://api.github.com/repos/niuhuan/pikapi-flutter/releases/latest";
|
"https://api.github.com/repos/niuhuan/pikapika/releases/latest";
|
||||||
const _versionAssets = 'lib/assets/version.txt';
|
const _versionAssets = 'lib/assets/version.txt';
|
||||||
RegExp _versionExp = RegExp(r"^v\d+\.\d+.\d+$");
|
RegExp _versionExp = RegExp(r"^v\d+\.\d+.\d+$");
|
||||||
|
|
||||||
late String _version;
|
late String _version;
|
||||||
var _latestVersion = "";
|
String? _latestVersion;
|
||||||
|
|
||||||
|
const _propertyName = "checkVersionPeriod";
|
||||||
|
late int _period = -1;
|
||||||
|
|
||||||
Future initVersion() async {
|
Future initVersion() async {
|
||||||
|
// 当前版本
|
||||||
try {
|
try {
|
||||||
_version = (await rootBundle.loadString(_versionAssets)).trim();
|
_version = (await rootBundle.loadString(_versionAssets)).trim();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_version = "dirty";
|
_version = "dirty";
|
||||||
}
|
}
|
||||||
|
// 检查周期
|
||||||
|
_period = int.parse(await method.loadProperty(_propertyName, "0"));
|
||||||
|
if (_period > 0) {
|
||||||
|
if (DateTime.now().millisecondsSinceEpoch > _period) {
|
||||||
|
await method.saveProperty(_propertyName, "0");
|
||||||
|
_period = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future autoCheckNewVersion() async {}
|
var versionEvent = Event<EventArgs>();
|
||||||
|
|
||||||
|
String currentVersion() {
|
||||||
|
return _version;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? latestVersion() {
|
||||||
|
return _latestVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future autoCheckNewVersion() {
|
||||||
|
if (_period != 0) {
|
||||||
|
// -1 不检查, >0 未到检查时间
|
||||||
|
return Future.value();
|
||||||
|
}
|
||||||
|
return _versionCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future manualCheckNewVersion(BuildContext context) async {
|
||||||
|
try {
|
||||||
|
defaultToast(context, "检查更新中");
|
||||||
|
await _versionCheck();
|
||||||
|
defaultToast(context, "检查更新成功");
|
||||||
|
} catch (e) {
|
||||||
|
defaultToast(context, "检查更新失败 : $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dirtyVersion() {
|
||||||
|
return !_versionExp.hasMatch(_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe exception
|
||||||
Future _versionCheck() async {
|
Future _versionCheck() async {
|
||||||
if (_versionExp.hasMatch(_version)) {
|
if (_versionExp.hasMatch(_version)) {
|
||||||
// exception
|
var json = jsonDecode(await method.httpGet(_versionUrl));
|
||||||
String latestVersion = (await method.httpGet(_versionUrl)).trim();
|
if (json["name"] != null) {
|
||||||
|
String latestVersion = (json["name"]);
|
||||||
if (latestVersion != _version) {
|
if (latestVersion != _version) {
|
||||||
// new Version
|
_latestVersion = latestVersion;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// dirtyVersion
|
|
||||||
}
|
}
|
||||||
//
|
} // else dirtyVersion
|
||||||
|
versionEvent.broadcast();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _periodText() {
|
||||||
|
if (_period < 0) {
|
||||||
|
return "自动检查更新已关闭";
|
||||||
|
}
|
||||||
|
if (_period == 0) {
|
||||||
|
return "自动检查更新已开启";
|
||||||
|
}
|
||||||
|
return "下次检查时间 : " +
|
||||||
|
formatDateTimeToDateTime(
|
||||||
|
DateTime.fromMillisecondsSinceEpoch(_period),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future _choosePeriod(BuildContext context) async {
|
||||||
|
var result = await chooseListDialog(
|
||||||
|
context,
|
||||||
|
"自动检查更新",
|
||||||
|
["开启", "一周后", "一个月后", "一年后", "关闭"],
|
||||||
|
tips: "重启后红点会消失",
|
||||||
|
);
|
||||||
|
switch (result) {
|
||||||
|
case "开启":
|
||||||
|
await method.saveProperty(_propertyName, "0");
|
||||||
|
_period = 0;
|
||||||
|
break;
|
||||||
|
case "一周后":
|
||||||
|
var time = DateTime.now().millisecondsSinceEpoch + (1000 * 3600 * 24 * 7);
|
||||||
|
await method.saveProperty(_propertyName, "$time");
|
||||||
|
_period = time;
|
||||||
|
break;
|
||||||
|
case "一个月后":
|
||||||
|
var time =
|
||||||
|
DateTime.now().millisecondsSinceEpoch + (1000 * 3600 * 24 * 30);
|
||||||
|
await method.saveProperty(_propertyName, "$time");
|
||||||
|
_period = time;
|
||||||
|
break;
|
||||||
|
case "一年后":
|
||||||
|
var time =
|
||||||
|
DateTime.now().millisecondsSinceEpoch + (1000 * 3600 * 24 * 365);
|
||||||
|
await method.saveProperty(_propertyName, "$time");
|
||||||
|
_period = time;
|
||||||
|
break;
|
||||||
|
case "关闭":
|
||||||
|
await method.saveProperty(_propertyName, "-1");
|
||||||
|
_period = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget autoUpdateCheckSetting() {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (BuildContext context, void Function(void Function()) setState) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text("自动检查更新"),
|
||||||
|
subtitle: Text(_periodText()),
|
||||||
|
onTap: () async {
|
||||||
|
await _choosePeriod(context);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatDateTimeToDateTime(DateTime c) {
|
||||||
|
try {
|
||||||
|
return "${add0(c.year, 4)}-${add0(c.month, 2)}-${add0(c.day, 2)} ${add0(c.hour, 2)}:${add0(c.minute, 2)}";
|
||||||
|
} catch (e) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,42 @@
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:pikapika/basic/Cross.dart';
|
||||||
|
import 'package:pikapika/basic/config/Version.dart';
|
||||||
|
import 'package:pikapika/screens/components/Badge.dart';
|
||||||
|
|
||||||
|
const _releasesUrl = "https://github.com/niuhuan/pikapika/releases";
|
||||||
|
|
||||||
// 关于
|
// 关于
|
||||||
class AboutScreen extends StatelessWidget {
|
class AboutScreen extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _AboutScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AboutScreenState extends State<AboutScreen> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
versionEvent.subscribe(_onVersion);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
versionEvent.unsubscribe(_onVersion);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVersion(dynamic a) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var size = MediaQuery.of(context).size;
|
var size = MediaQuery.of(context).size;
|
||||||
var min = size.width < size.height ? size.width : size.height;
|
var min = size.width < size.height ? size.width : size.height;
|
||||||
|
var _currentVersion = currentVersion();
|
||||||
|
var _latestVersion = latestVersion();
|
||||||
|
var _dirty = dirtyVersion();
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('关于'),
|
title: Text('关于'),
|
||||||
|
@ -26,15 +55,37 @@ class AboutScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Container(height: 20),
|
||||||
|
Divider(),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.only(left: 20, right: 20),
|
||||||
child: Text(
|
child: Column(
|
||||||
'请从软件取得渠道获取更新\n本软件开源, 若您想提出改进建议或者获取源码, 请在开源社区搜索 pikapi',
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'软件版本 : $_currentVersion',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
height: 1.3,
|
height: 1.3,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"检查更新 : ",
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1.3,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
_dirty ? _buildDirty() : _buildNewVersion(_latestVersion),
|
||||||
|
Expanded(child: Container()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
|
autoUpdateCheckSetting(),
|
||||||
|
Divider(),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
|
@ -49,8 +100,81 @@ class AboutScreen extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Divider(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_buildNewVersion(String? latestVersion) {
|
||||||
|
if (latestVersion != null) {
|
||||||
|
return Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
WidgetSpan(
|
||||||
|
child: Badged(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(right: 12),
|
||||||
|
child: Text(
|
||||||
|
latestVersion,
|
||||||
|
style: TextStyle(height: 1.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
badge: "1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(text: " "),
|
||||||
|
TextSpan(
|
||||||
|
text: "去下载",
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1.3,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () => openUrl(_releasesUrl),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
TextSpan(text: "未检测到新版本", style: TextStyle(height: 1.3)),
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.middle,
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(4),
|
||||||
|
margin: EdgeInsets.only(left: 3, right: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: "检查更新",
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1.3,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () => manualCheckNewVersion(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDirty() {
|
||||||
|
return Text.rich(
|
||||||
|
TextSpan(
|
||||||
|
text: "下载RELEASE版",
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1.3,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
recognizer: TapGestureRecognizer()..onTap = () => openUrl(_releasesUrl),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pikapika/basic/config/Version.dart';
|
||||||
|
import 'package:pikapika/screens/components/Badge.dart';
|
||||||
|
|
||||||
import 'CategoriesScreen.dart';
|
import 'CategoriesScreen.dart';
|
||||||
import 'SpaceScreen.dart';
|
import 'SpaceScreen.dart';
|
||||||
|
@ -12,20 +14,26 @@ class AppScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppScreenState extends State<AppScreen> {
|
class _AppScreenState extends State<AppScreen> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
versionEvent.subscribe(_onVersion);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
versionEvent.unsubscribe(_onVersion);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVersion(dynamic a) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
static const List<Widget> _widgetOptions = <Widget>[
|
static const List<Widget> _widgetOptions = <Widget>[
|
||||||
const CategoriesScreen(),
|
const CategoriesScreen(),
|
||||||
const SpaceScreen(),
|
const SpaceScreen(),
|
||||||
];
|
];
|
||||||
static const _navigationItems = <BottomNavigationBarItem>[
|
|
||||||
const BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.public),
|
|
||||||
label: '浏览',
|
|
||||||
),
|
|
||||||
const BottomNavigationBarItem(
|
|
||||||
icon: Icon(Icons.face),
|
|
||||||
label: '我的',
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
late int _selectedIndex = 0;
|
late int _selectedIndex = 0;
|
||||||
|
|
||||||
|
@ -43,7 +51,19 @@ class _AppScreenState extends State<AppScreen> {
|
||||||
children: _widgetOptions,
|
children: _widgetOptions,
|
||||||
),
|
),
|
||||||
bottomNavigationBar: BottomNavigationBar(
|
bottomNavigationBar: BottomNavigationBar(
|
||||||
items: _navigationItems,
|
items: <BottomNavigationBarItem>[
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Icon(Icons.public),
|
||||||
|
label: '浏览',
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
icon: Badged(
|
||||||
|
child: Icon(Icons.face),
|
||||||
|
badge: latestVersion() == null ? null : "1",
|
||||||
|
),
|
||||||
|
label: '我的',
|
||||||
|
),
|
||||||
|
],
|
||||||
currentIndex: _selectedIndex,
|
currentIndex: _selectedIndex,
|
||||||
iconSize: 20,
|
iconSize: 20,
|
||||||
selectedFontSize: 12,
|
selectedFontSize: 12,
|
||||||
|
|
|
@ -71,7 +71,7 @@ class _InitScreenState extends State<InitScreen> {
|
||||||
await initDownloadThreadCount();
|
await initDownloadThreadCount();
|
||||||
await initConvertToPNG();
|
await initConvertToPNG();
|
||||||
await initVersion();
|
await initVersion();
|
||||||
await autoCheckNewVersion();
|
autoCheckNewVersion();
|
||||||
// 登录, 如果token失效重新登录, 网络不好的时候可能需要1分钟
|
// 登录, 如果token失效重新登录, 网络不好的时候可能需要1分钟
|
||||||
if (await method.preLogin()) {
|
if (await method.preLogin()) {
|
||||||
// 如果token或username+password有效则直接进入登录好的界面
|
// 如果token或username+password有效则直接进入登录好的界面
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -22,6 +21,7 @@ import 'package:pikapika/basic/config/Quality.dart';
|
||||||
import 'package:pikapika/basic/config/ShadowCategories.dart';
|
import 'package:pikapika/basic/config/ShadowCategories.dart';
|
||||||
import 'package:pikapika/basic/config/Themes.dart';
|
import 'package:pikapika/basic/config/Themes.dart';
|
||||||
import 'package:pikapika/basic/config/TimeOffsetHour.dart';
|
import 'package:pikapika/basic/config/TimeOffsetHour.dart';
|
||||||
|
import 'package:pikapika/basic/config/Version.dart';
|
||||||
import 'package:pikapika/basic/config/VolumeController.dart';
|
import 'package:pikapika/basic/config/VolumeController.dart';
|
||||||
import 'package:pikapika/screens/components/NetworkSetting.dart';
|
import 'package:pikapika/screens/components/NetworkSetting.dart';
|
||||||
|
|
||||||
|
@ -72,6 +72,9 @@ class SettingsScreen extends StatelessWidget {
|
||||||
fontSetting(),
|
fontSetting(),
|
||||||
Divider(),
|
Divider(),
|
||||||
migrate(context),
|
migrate(context),
|
||||||
|
Divider(),
|
||||||
|
autoUpdateCheckSetting(),
|
||||||
|
Divider(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:pikapika/basic/Common.dart';
|
import 'package:pikapika/basic/Common.dart';
|
||||||
import 'package:pikapika/basic/config/Themes.dart';
|
import 'package:pikapika/basic/config/Themes.dart';
|
||||||
|
import 'package:pikapika/basic/config/Version.dart';
|
||||||
import 'package:pikapika/screens/AboutScreen.dart';
|
import 'package:pikapika/screens/AboutScreen.dart';
|
||||||
import 'package:pikapika/screens/AccountScreen.dart';
|
import 'package:pikapika/screens/AccountScreen.dart';
|
||||||
import 'package:pikapika/screens/DownloadListScreen.dart';
|
import 'package:pikapika/screens/DownloadListScreen.dart';
|
||||||
|
@ -9,6 +10,7 @@ import 'package:pikapika/screens/ViewLogsScreen.dart';
|
||||||
import 'package:pikapika/basic/Method.dart';
|
import 'package:pikapika/basic/Method.dart';
|
||||||
|
|
||||||
import 'SettingsScreen.dart';
|
import 'SettingsScreen.dart';
|
||||||
|
import 'components/Badge.dart';
|
||||||
import 'components/UserProfileCard.dart';
|
import 'components/UserProfileCard.dart';
|
||||||
|
|
||||||
// 个人空间页面
|
// 个人空间页面
|
||||||
|
@ -20,11 +22,23 @@ class SpaceScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SpaceScreenState extends State<SpaceScreen> {
|
class _SpaceScreenState extends State<SpaceScreen> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
versionEvent.subscribe(_onVersion);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
versionEvent.unsubscribe(_onVersion);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onVersion(dynamic a) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -53,7 +67,10 @@ class _SpaceScreenState extends State<SpaceScreen> {
|
||||||
MaterialPageRoute(builder: (context) => AboutScreen()),
|
MaterialPageRoute(builder: (context) => AboutScreen()),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: Icon(Icons.info_outline),
|
icon: Badged(
|
||||||
|
child: Icon(Icons.info_outline),
|
||||||
|
badge: latestVersion() == null ? null : "1",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class Badged extends StatelessWidget {
|
||||||
|
final String? badge;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const Badged({Key? key, required this.child, this.badge}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (badge == null) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
return Stack(
|
||||||
|
children: [
|
||||||
|
child,
|
||||||
|
new Positioned(
|
||||||
|
right: 0,
|
||||||
|
child: new Container(
|
||||||
|
padding: EdgeInsets.all(1),
|
||||||
|
decoration: new BoxDecoration(
|
||||||
|
color: Colors.red,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: 12,
|
||||||
|
minHeight: 12,
|
||||||
|
),
|
||||||
|
child: new Text(
|
||||||
|
badge!,
|
||||||
|
style: new TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 8,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,7 @@
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "pikapi.app"
|
BuildableName = "pikapika.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
|
@ -31,13 +31,13 @@
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "pikapi.app"
|
BuildableName = "pikapika.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<Testables>
|
||||||
</AdditionalOptions>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
|
@ -54,13 +54,11 @@
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "pikapi.app"
|
BuildableName = "pikapika.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Profile"
|
buildConfiguration = "Profile"
|
||||||
|
@ -73,7 +71,7 @@
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||||
BuildableName = "pikapi.app"
|
BuildableName = "pikapika.app"
|
||||||
BlueprintName = "Runner"
|
BlueprintName = "Runner"
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
ReferencedContainer = "container:Runner.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
|
|
Loading…
Reference in New Issue