Reason Pun

增加了权限申请页面和隐私提示页面

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
/// 创建人: Created by zhaolong
/// 创建时间:Created by on 2020/12/9.
///
/// 可关注公众号:我的大前端生涯 获取最新技术分享
/// 可关注网易云课堂:https://study.163.com/instructor/1021406098.htm
/// 可关注博客:https://blog.csdn.net/zl18603543572
///
/// 代码清单
///权限请求模版
class PermissionRequestWidget extends StatefulWidget {
final Permission permission;
final List<String> permissionList;
final bool isCloseApp;
final String leftButtonText;
const PermissionRequestWidget({
Key? key,
required this.permission,
required this.permissionList,
this.leftButtonText = "再考虑一下",
this.isCloseApp = false,
}) : super(key: key);
@override
_PermissionRequestWidgetState createState() =>
_PermissionRequestWidgetState();
}
class _PermissionRequestWidgetState extends State<PermissionRequestWidget>
with WidgetsBindingObserver {
//页面的初始化函数
@override
void initState() {
super.initState();
checkPermission();
//注册观察者
if (WidgetsBinding.instance != null) {
WidgetsBinding.instance!.addObserver(this);
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.resumed && _isGoSetting) {
checkPermission();
}
}
///[PermissionStatus.denied] 用户拒绝访问所请求的特性
///[PermissionStatus.granted] 用户被授予对所请求特性的访问权。
///[PermissionStatus.restricted] iOS 平台 用户拒绝这个权限
///[PermissionStatus.limited] 用户已授权此应用程序进行有限访问。
///[PermissionStatus.permanentlyDenied] 被永久拒绝
void checkPermission({PermissionStatus? status}) async {
//权限
Permission permission = widget.permission;
status ??= await permission.status;
if (status.isGranted) {
Navigator.of(context).pop(true);
return;
}
if (!status.isLimited) {
//第一次申请
showPermissionAlert(widget.permissionList[0], "同意", permission);
} else if (status.isDenied) {
if (Platform.isIOS) {
showPermissionAlert(widget.permissionList[2], "去设置中心", permission,
isSetting: true);
return;
}
//用户第一次申请拒绝
showPermissionAlert(widget.permissionList[1], "重试", permission);
} else if (status.isPermanentlyDenied) {
//第二次申请 用户拒绝
showPermissionAlert(widget.permissionList[2], "去设置中心", permission,
isSetting: true);
} else {
//通过
Navigator.of(context).pop(true);
}
}
//是否去设置中心
bool _isGoSetting = false;
void showPermissionAlert(
String message,
String rightString,
Permission permission, {
bool isSetting = false,
}) {
showDialog(
builder: (BuildContext context) {
return AlertDialog(
title: const Text("温馨提示"),
content: Container(
padding: const EdgeInsets.all(12),
child: Text(message),
),
actions: [
//左边的按钮
TextButton(
child: Text(widget.leftButtonText),
onPressed: () {
if (widget.isCloseApp) {
closeApp();
} else {
Navigator.of(context).pop(false);
}
},
),
//右边的按钮
TextButton(
child: Text(rightString),
onPressed: () {
//关闭弹框
Navigator.of(context).pop();
if (isSetting) {
_isGoSetting = true;
//去设置中心
openAppSettings();
} else {
//申请权限
requestPermission(permission);
}
},
)
],
);
},
context: context);
}
void requestPermission(Permission permission) async {
//发起权限申请
PermissionStatus status = await permission.request();
//校验
checkPermission();
}
/// TODO 暂未使用
void requestPermissionList(List<Permission> list) async {
//多个权限申请
Map<Permission, PermissionStatus> statuses = await [
Permission.location,
Permission.storage,
].request();
}
void closeApp() {
//关闭应用的方法
SystemChannels.platform.invokeMethod("SystemNavigator.pop");
}
@override
void dispose() {
//注销观察者
if (WidgetsBinding.instance != null) {
WidgetsBinding.instance!.removeObserver(this);
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return const Scaffold(
backgroundColor: Colors.transparent,
);
}
}
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:one_poem/res/constant.dart';
import 'package:one_poem/routers/fluro_navigator.dart';
/// 创建人: Created by zhaolong
/// 创建时间:Created by on 2020/12/12.
///
/// 可关注公众号:我的大前端生涯 获取最新技术分享
/// 可关注网易云课堂:https://study.163.com/instructor/1021406098.htm
/// 可关注博客:https://blog.csdn.net/zl18603543572
///
/// 代码清单
///代码清单
class ProtocolModel {
late TapGestureRecognizer _registerProtocolRecognizer;
late TapGestureRecognizer _privacyProtocolRecognizer;
///用来显示 用户协议对话框
Future<bool> showProtocolFunction(BuildContext context) async {
//注册协议的手势
_registerProtocolRecognizer = TapGestureRecognizer();
//隐私协议的手势
_privacyProtocolRecognizer = TapGestureRecognizer();
//苹果风格弹框
bool isShow = await showDialog(
//上下文对象
context: context,
//对话框内容
builder: (BuildContext context) {
return showAlertDialog(context);
},
);
///销毁
_registerProtocolRecognizer.dispose();
_privacyProtocolRecognizer.dispose();
return Future.value(isShow);
}
AlertDialog showAlertDialog(BuildContext context) {
return AlertDialog(
title: const Text("温馨提示"),
content: Container(
height: 260,
padding: const EdgeInsets.all(1),
//可滑动布局
child: SingleChildScrollView(
child: buildContent(context),
),
),
actions: [
TextButton(
child: const Text("不同意"),
onPressed: () {
Navigator.of(context).pop(false);
},
),
TextButton(
child: const Text("同意"),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
);
}
//协议说明文案
String userPrivateProtocol =
"我们一向尊重并会严格保护用户在使用本产品时的合法权益(包括用户隐私、用户数据等)不受到任何侵犯。本协议(包括本文最后部分的隐私政策)是用户(包括通过各种合法途径获取到本产品的自然人、法人或其他组织机构,以下简称“用户”或“您”)与我们之间针对本产品相关事项最终的、完整的且排他的协议,并取代、合并之前的当事人之间关于上述事项的讨论和协议。本协议将对用户使用本产品的行为产生法律约束力,您已承诺和保证有权利和能力订立本协议。用户开始使用本产品将视为已经接受本协议,请认真阅读并理解本协议中各种条款,包括免除和限制我们的免责条款和对用户的权利限制(未成年人审阅时应由法定监护人陪同),如果您不能接受本协议中的全部条款,请勿开始使用本产品";
buildContent(BuildContext context) {
return RichText(
text: TextSpan(
text: "请您在使用本产品之前仔细阅读",
style: TextStyle(color: Colors.grey[600]),
children: [
TextSpan(
text: "《用户协议》",
style: const TextStyle(color: Colors.blue),
//点击事件
recognizer: _registerProtocolRecognizer
..onTap = () {
//打开用户协议
openUserProtocol(context);
},
),
TextSpan(
text: "与",
style: TextStyle(color: Colors.grey[600]),
),
TextSpan(
text: "《隐私协议》",
style: const TextStyle(color: Colors.blue),
//点击事件
recognizer: _privacyProtocolRecognizer
..onTap = () {
//打开隐私协议
openPrivateProtocol(context);
},
),
TextSpan(
text: userPrivateProtocol,
style: TextStyle(color: Colors.grey[600]),
),
]),
);
}
//查看用户协议
void openUserProtocol(BuildContext context) {
NavigatorUtils.goWebViewPage(
context,
'用户协议',
Constant.protocolUrl,
);
}
//查看隐私协议
void openPrivateProtocol(BuildContext context) {
NavigatorUtils.goWebViewPage(
context,
'隐私协议',
Constant.privacyUrl,
);
}
}
import 'package:flustars/flustars.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:one_poem/common/permission_request_widget.dart';
import 'package:one_poem/common/protocol_model.dart';
import 'package:one_poem/home/webview_page.dart';
import 'package:one_poem/login/widgets/my_text_field.dart';
import 'package:one_poem/res/constant.dart';
import 'package:one_poem/res/resources.dart';
......@@ -18,6 +22,8 @@ import '../login_router.dart';
import 'package:flutter_gen/gen_l10n/one_poem_localizations.dart';
import 'package:one_poem/extension/int_extension.dart';
import 'package:permission_handler/permission_handler.dart';
/// design/1注册登录/index.html
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
......@@ -27,7 +33,7 @@ class LoginPage extends StatefulWidget {
}
class _LoginPageState extends State<LoginPage>
with ChangeNotifierMixin<LoginPage> {
with ChangeNotifierMixin<LoginPage>, ProtocolModel {
//定义一个controller
final TextEditingController _nameController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
......@@ -55,6 +61,40 @@ class _LoginPageState extends State<LoginPage>
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]);
});
_nameController.text = SpUtil.getString(Constant.phone).nullSafe;
List<String> _list = [
"为您更好的体验应用,所以需要获取您的手机文件存储权限,以保存您的一些偏好设置",
"您已拒绝权限,所以无法保存您的一些偏好设置,将无法使用APP",
"您已拒绝权限,请在设置中心中同意APP的权限请求",
"其他错误"
];
Future.delayed(Duration.zero, () {
NavigatorUtils.pushPageByFade(
context: context,
//目标页面
targetPage: PermissionRequestWidget(
//所需要申请的权限
permission: Permission.camera,
//显示关闭应用按钮
isCloseApp: true,
//提示文案
permissionList: _list,
),
//权限申请结果
dismissCallBack: (value) {
showPrivacyPage();
});
});
}
void showPrivacyPage() async {
bool isAgreement = await showProtocolFunction(context);
if (isAgreement) {
// next();
} else {
SystemChannels.platform.invokeMethod("SystemNavigator.pop");
}
}
void _verify() {
......@@ -125,6 +165,46 @@ class _LoginPageState extends State<LoginPage>
hintText: OnePoemLocalizations.of(context).inputPasswordHint,
),
Gaps.vGap24,
Text.rich(
TextSpan(
text: '登录即代表同意并阅读',
style: const TextStyle(fontSize: 12, color: Color(0xFF999999)),
children: [
TextSpan(
text: '《用户协议》',
style: TextStyle(color: Theme.of(context).primaryColor),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return const WebViewPage(
title: '《用户协议》',
url: Constant.protocolUrl,
);
}));
},
),
const TextSpan(text: '和'),
TextSpan(
text: '《隐私政策》',
style: TextStyle(color: Theme.of(context).primaryColor),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) {
return const WebViewPage(
title: '《隐私政策》',
url: Constant.privacyUrl,
);
},
),
);
},
),
]),
),
Gaps.vGap16,
MyButton(
key: const Key('login'),
onPressed: _clickable ? _login : null,
......
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:one_poem/home/webview_page.dart';
import 'package:one_poem/login/widgets/my_text_field.dart';
import 'package:one_poem/res/resources.dart';
import 'package:one_poem/res/styles.dart';
......@@ -32,6 +34,7 @@ class _RegisterPageState extends State<RegisterPage>
final FocusNode _nodeText2 = FocusNode();
final FocusNode _nodeText3 = FocusNode();
bool _clickable = false;
bool _switchSelected=true;
@override
Map<ChangeNotifier, List<VoidCallback>?>? changeNotifier() {
......@@ -130,6 +133,39 @@ class _RegisterPageState extends State<RegisterPage>
keyboardType: TextInputType.visiblePassword,
hintText: OnePoemLocalizations.of(context).inputPasswordHint,
),
Gaps.vGap8,
Text.rich(
TextSpan(
text: '登录即代表同意并阅读',
style: TextStyle(fontSize: 14, color: const Color(0xFF999999)),
children: [
TextSpan(
text: '《用户协议》',
style: TextStyle(color: Theme.of(context).primaryColor),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return WebViewPage(
title: '《用户协议》', url: 'https://flutter.dev');
}));
},
),
TextSpan(text: '和'),
TextSpan(
text: '《隐私政策》',
style: TextStyle(color: Theme.of(context).primaryColor),
recognizer: TapGestureRecognizer()
..onTap = () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return WebViewPage(
title: '《隐私政策》', url: 'https://flutter.dev');
}));
},
),
]),
),
Gaps.vGap24,
MyButton(
key: const Key('register'),
......
......@@ -2,6 +2,9 @@ import 'package:flutter/foundation.dart';
class Constant {
static const String privacyUrl = "https://www.mofunenglish.com/index.php?act=about&mdl=agreement";
static const String protocolUrl = "https://www.mofunenglish.com/index.php?act=about&mdl=agreement";
/// App运行在Release环境时,inProduction为true;当App运行在Debug和Profile环境时,inProduction为false
static const bool inProduction = kReleaseMode;
......
......@@ -5,11 +5,12 @@ import 'routers.dart';
/// fluro的路由跳转工具类
class NavigatorUtils {
static void push(BuildContext context, String path,
{bool replace = false, bool clearStack = false, Object? arguments}) {
unfocus();
Routes.router.navigateTo(context, path,
Routes.router.navigateTo(
context,
path,
replace: replace,
clearStack: clearStack,
transition: TransitionType.native,
......@@ -19,17 +20,22 @@ class NavigatorUtils {
);
}
static void pushResult(BuildContext context, String path, Function(Object) function,
static void pushResult(
BuildContext context, String path, Function(Object) function,
{bool replace = false, bool clearStack = false, Object? arguments}) {
unfocus();
Routes.router.navigateTo(context, path,
Routes.router
.navigateTo(
context,
path,
replace: replace,
clearStack: clearStack,
transition: TransitionType.native,
routeSettings: RouteSettings(
arguments: arguments,
),
).then((Object? result) {
)
.then((Object? result) {
// 页面返回result为null
if (result == null) {
return;
......@@ -55,7 +61,8 @@ class NavigatorUtils {
/// 跳到WebView页
static void goWebViewPage(BuildContext context, String title, String url) {
//fluro 不支持传中文,需转换
push(context, '${Routes.webViewPage}?title=${Uri.encodeComponent(title)}&url=${Uri.encodeComponent(url)}');
push(context,
'${Routes.webViewPage}?title=${Uri.encodeComponent(title)}&url=${Uri.encodeComponent(url)}');
}
static void unfocus() {
......@@ -64,4 +71,50 @@ class NavigatorUtils {
// https://github.com/flutter/flutter/issues/47128#issuecomment-627551073
FocusManager.instance.primaryFocus?.unfocus();
}
///普通打开页面的方法
///[context] 上下文对象
///[targetPage] 目标页面
///[isReplace] 是否替换当前页面 A -B
///[opaque] 是否以背景透明的方式打开页面
static void pushPageByFade({
required BuildContext context,
required Widget targetPage,
bool isReplace = false,
int startMills = 400,
bool opaque = false,
Function(dynamic value)? dismissCallBack,
}) {
PageRoute pageRoute = PageRouteBuilder(
//背景透明 方式打开新的页面
opaque: opaque,
pageBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return targetPage;
},
transitionDuration: Duration(milliseconds: startMills),
//动画
transitionsBuilder: (BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
);
if (isReplace) {
Navigator.of(context).pushReplacement(pageRoute).then((value) {
if (dismissCallBack != null) {
dismissCallBack(value);
}
});
} else {
Navigator.of(context).push(pageRoute).then((value) {
if (dismissCallBack != null) {
dismissCallBack(value);
}
});
}
}
}
......
......@@ -708,6 +708,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
permission_handler:
dependency: "direct dev"
description:
name: permission_handler
url: "https://pub.dartlang.org"
source: hosted
version: "8.3.0"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.7.0"
petitparser:
dependency: transitive
description:
......
......@@ -129,6 +129,8 @@ dev_dependencies:
flutter_launcher_icons: ^0.9.2
flutter_native_splash: ^1.3.2
permission_handler: ^8.3.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
......