add flutter_whatsnew
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
library flutter_whatsnew;
|
||||
|
||||
export 'src/base.dart';
|
||||
export 'src/changelog.dart';
|
||||
export 'src/scheduled.dart';
|
||||
@@ -0,0 +1,238 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
|
||||
import 'changelog.dart';
|
||||
|
||||
class WhatsNewPage extends StatelessWidget {
|
||||
final Widget title;
|
||||
final Widget buttonText;
|
||||
final List<ListTile>? items;
|
||||
final VoidCallback? onButtonPressed;
|
||||
final bool changelog;
|
||||
final String? changes;
|
||||
final Color? backgroundColor;
|
||||
final Color? buttonColor;
|
||||
final String? path;
|
||||
final MarkdownTapLinkCallback? onTapLink;
|
||||
final bool adaptive;
|
||||
|
||||
const WhatsNewPage({
|
||||
required this.items,
|
||||
required this.title,
|
||||
required this.buttonText,
|
||||
this.onButtonPressed,
|
||||
this.backgroundColor,
|
||||
this.buttonColor,
|
||||
this.onTapLink,
|
||||
this.adaptive = true,
|
||||
}) : changelog = false,
|
||||
changes = null,
|
||||
path = null;
|
||||
|
||||
const WhatsNewPage.changelog({
|
||||
this.title = const Text(
|
||||
'Changelog',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 22.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
this.buttonText = const Text(
|
||||
'Continue',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
this.onButtonPressed,
|
||||
this.changes,
|
||||
this.backgroundColor,
|
||||
this.buttonColor,
|
||||
this.path,
|
||||
this.onTapLink,
|
||||
this.adaptive = true,
|
||||
}) : changelog = true,
|
||||
items = null;
|
||||
|
||||
static void showDetailPopUp(
|
||||
BuildContext context,
|
||||
String title,
|
||||
String detail,
|
||||
) async {
|
||||
void showDemoDialog<T>({
|
||||
required BuildContext context,
|
||||
required Widget child,
|
||||
}) {
|
||||
showDialog<T>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) => child,
|
||||
);
|
||||
}
|
||||
|
||||
return showDemoDialog<Null>(
|
||||
context: context,
|
||||
child: AlertDialog(
|
||||
title: Text(title),
|
||||
content: Text(detail),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text('OK'),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(items != null || changelog);
|
||||
if (adaptive &&
|
||||
!kIsWeb &&
|
||||
(defaultTargetPlatform == TargetPlatform.iOS ||
|
||||
defaultTargetPlatform == TargetPlatform.macOS)) {
|
||||
return _buildIOS(context);
|
||||
}
|
||||
return _buildAndroid(context);
|
||||
}
|
||||
|
||||
Widget _buildAndroid(BuildContext context) {
|
||||
final buttonStyle = ElevatedButton.styleFrom(
|
||||
backgroundColor: buttonColor ?? Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: buttonColor != null
|
||||
? (buttonColor!.computeLuminance() > 0.5
|
||||
? Colors.black
|
||||
: Colors.white)
|
||||
: Theme.of(context).colorScheme.onPrimary,
|
||||
);
|
||||
if (changelog) {
|
||||
return Scaffold(
|
||||
backgroundColor:
|
||||
backgroundColor ?? Theme.of(context).scaffoldBackgroundColor,
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
fit: StackFit.loose,
|
||||
children: <Widget>[
|
||||
Positioned(
|
||||
top: 10.0,
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
child: title,
|
||||
),
|
||||
Positioned(
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
top: 50.0,
|
||||
bottom: 80.0,
|
||||
child: ChangeLogView(
|
||||
changes: changes,
|
||||
path: path,
|
||||
onTapLink: onTapLink,
|
||||
adaptive: adaptive,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 5.0,
|
||||
right: 10.0,
|
||||
left: 10.0,
|
||||
child: ListTile(
|
||||
title: ElevatedButton(
|
||||
child: buttonText,
|
||||
style: buttonStyle,
|
||||
onPressed: onButtonPressed != null
|
||||
? onButtonPressed
|
||||
: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (items != null) {
|
||||
return Scaffold(
|
||||
backgroundColor:
|
||||
backgroundColor ?? Theme.of(context).scaffoldBackgroundColor,
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
fit: StackFit.loose,
|
||||
children: <Widget>[
|
||||
Positioned(
|
||||
top: 10.0,
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
child: title,
|
||||
),
|
||||
Positioned(
|
||||
left: 0.0,
|
||||
right: 0.0,
|
||||
top: 50.0,
|
||||
bottom: 80.0,
|
||||
child: ListView(
|
||||
children: items!
|
||||
.map(
|
||||
(ListTile item) => ListTile(
|
||||
title: item.title,
|
||||
subtitle: item.subtitle,
|
||||
leading: item.leading,
|
||||
trailing: item.trailing,
|
||||
onTap: item.onTap,
|
||||
onLongPress: item.onLongPress,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 5.0,
|
||||
right: 10.0,
|
||||
left: 10.0,
|
||||
child: ListTile(
|
||||
title: ElevatedButton(
|
||||
child: buttonText,
|
||||
style: buttonStyle,
|
||||
onPressed: onButtonPressed != null
|
||||
? onButtonPressed
|
||||
: () => Navigator.pop(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}
|
||||
|
||||
Widget _buildIOS(BuildContext context) {
|
||||
Widget? child;
|
||||
if (changelog) {
|
||||
child = ChangeLogView(
|
||||
changes: changes,
|
||||
path: path,
|
||||
onTapLink: onTapLink,
|
||||
adaptive: adaptive,
|
||||
);
|
||||
} else if (items != null) {
|
||||
child = Material(
|
||||
child: ListView(
|
||||
children: items!,
|
||||
),
|
||||
);
|
||||
}
|
||||
return CupertinoPageScaffold(
|
||||
navigationBar: CupertinoNavigationBar(
|
||||
middle: title,
|
||||
),
|
||||
child: SafeArea(
|
||||
child: child ?? Container(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
|
||||
class ChangeLogView extends StatefulWidget {
|
||||
const ChangeLogView({
|
||||
this.onTapLink,
|
||||
this.path,
|
||||
this.changes,
|
||||
required this.adaptive,
|
||||
});
|
||||
final String? changes;
|
||||
final String? path;
|
||||
final bool adaptive;
|
||||
final MarkdownTapLinkCallback? onTapLink;
|
||||
@override
|
||||
_ChangeLogViewState createState() => _ChangeLogViewState();
|
||||
}
|
||||
|
||||
class _ChangeLogViewState extends State<ChangeLogView> {
|
||||
String? _changelog;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.changes == null) {
|
||||
rootBundle.loadString(widget.path ?? "CHANGELOG.md").then((data) {
|
||||
setState(() {
|
||||
_changelog = data;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_changelog = widget.changes;
|
||||
});
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_changelog == null) {
|
||||
if (widget.adaptive &&
|
||||
(defaultTargetPlatform == TargetPlatform.iOS ||
|
||||
defaultTargetPlatform == TargetPlatform.macOS)) {
|
||||
return Center(child: CupertinoActivityIndicator());
|
||||
} else {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
}
|
||||
return Markdown(
|
||||
data: _changelog!,
|
||||
onTapLink: widget.onTapLink,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'base.dart';
|
||||
|
||||
class ScheduledWhatsNewPage extends StatefulWidget {
|
||||
const ScheduledWhatsNewPage({
|
||||
Key? key,
|
||||
required this.details,
|
||||
this.delay,
|
||||
this.appVersion,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
final WhatsNewPage details;
|
||||
final Widget child;
|
||||
|
||||
/// Wait a set duration and show the dialog
|
||||
final Duration? delay;
|
||||
|
||||
/// Check the last saved version and show the dialog if different (or fresh)
|
||||
final String? appVersion;
|
||||
|
||||
@override
|
||||
State<ScheduledWhatsNewPage> createState() => _ScheduledWhatsNewPageState();
|
||||
}
|
||||
|
||||
class _ScheduledWhatsNewPageState extends State<ScheduledWhatsNewPage> {
|
||||
late SharedPreferences prefs;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
SharedPreferences.getInstance().then((value) {
|
||||
prefs = value;
|
||||
check();
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void check() async {
|
||||
if (widget.appVersion != null) {
|
||||
final settingsKey = 'last-app-version';
|
||||
final lastVersion = prefs.getString(settingsKey);
|
||||
if (lastVersion == widget.appVersion) {
|
||||
return;
|
||||
}
|
||||
await prefs.setString(settingsKey, widget.appVersion!);
|
||||
}
|
||||
if (widget.delay != null) {
|
||||
await Future<void>.delayed(widget.delay!);
|
||||
}
|
||||
show(context);
|
||||
}
|
||||
|
||||
void show(BuildContext context) {
|
||||
if (!mounted) return;
|
||||
final nav = Navigator.of(context, rootNavigator: true);
|
||||
nav.push(MaterialPageRoute(
|
||||
builder: (context) => widget.details,
|
||||
fullscreenDialog: true,
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user