Chad

替换内购插件,重新实现内购服务

import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
abstract class BaseState<T extends StatefulWidget> extends State<T> {
bool _isFirstBuild = true;
@override
Widget build(BuildContext context) {
if (_isFirstBuild) {
onFirstBuildBody(context);
_isFirstBuild = false;
}
return buildBody(context);
}
Widget buildBody(BuildContext context);
void onFirstBuildBody(BuildContext context){
}
showLoading({String text = 'Loading...'}) {
EasyLoading.show(status: text);
}
hideLoading() {
EasyLoading.dismiss();
}
}
......@@ -8,4 +8,115 @@ extension WidgetExt on Widget {
SafeArea safe() {
return SafeArea(child: this);
}
ClipRRect round({double? radius, BorderRadius? borderRadius}) {
return ClipRRect(
borderRadius: borderRadius ?? BorderRadius.all(Radius.circular(radius ?? 5)),
child: this,
);
}
Container height(double height, {Alignment? alignment}) {
return Container(height: height, alignment: alignment, child: this);
}
Container height48({Alignment? alignment}) {
return Container(height: 48, alignment: alignment, child: this);
}
Container paddingALL(double padding) {
return Container(child: this, padding: EdgeInsets.all(padding));
}
Container paddingTopBottom(double padding) {
return Container(child: this, padding: EdgeInsets.only(bottom: padding, top: padding));
}
Container paddingBottom(double padding) {
return Container(child: this, padding: EdgeInsets.only(bottom: padding));
}
Container paddingTop(double padding) {
return Container(child: this, padding: EdgeInsets.only(top: padding));
}
Widget paddingLeftRight(double padding) {
return Container(child: this, padding: EdgeInsets.only(left: padding, right: padding));
}
Container paddingLeft(double padding) {
return Container(child: this, padding: EdgeInsets.only(left: padding));
}
Container paddingRight(double padding) {
return Container(child: this, padding: EdgeInsets.only(right: padding));
}
Row rowRight() {
return Row(mainAxisAlignment: MainAxisAlignment.end, children: [this]);
}
Row rowLeft() {
return Row(mainAxisAlignment: MainAxisAlignment.start, children: [this]);
}
Row rowCenter() {
return Row(mainAxisAlignment: MainAxisAlignment.center, children: [this]);
}
Widget click(
GestureTapCallback? onTap, {
Color? color,
double? radius,
BorderRadius? borderRadius,
Color? bgColor,
}) {
if (color != null) {
var border = radius == null ? null : BorderRadius.all(Radius.circular(radius));
return getMaterialInkWell(
this,
onTap,
color,
borderRadius: borderRadius ?? border,
bgColor: bgColor,
);
}
return getWhiteInkWell(this, onTap);
}
///当InkWell效果失效时,使用这个套件
Widget getMaterialInkWell(
Widget widget,
GestureTapCallback? onTap,
Color? color, {
BorderRadius? borderRadius,
Color? splashColor,
Color? bgColor = Colors.white,
}) {
return Material(
color: bgColor,
child: Ink(
decoration: BoxDecoration(
color: color,
borderRadius: borderRadius,
),
child: InkWell(
onTap: onTap,
splashColor: splashColor,
borderRadius: borderRadius,
child: widget,
),
),
);
}
///当InkWell效果失效时,使用这个套件
Widget getWhiteInkWell(Widget widget, GestureTapCallback? onTap, {BorderRadius? borderRadius}) {
return Material(
child: Ink(
color: Colors.white,
child: InkWell(onTap: onTap, child: widget),
),
);
}
}
......
......@@ -6,6 +6,7 @@ import 'package:dio/dio.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:oktoast/oktoast.dart';
import 'package:provider/provider.dart';
import 'package:quick_actions/quick_actions.dart';
......@@ -60,8 +61,7 @@ Future<void> main() async {
await SpUtil.getInstance();
WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setPreferredOrientations(
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
/// 1.22 预览功能: 在输入频率与显示刷新率不匹配情况下提供平滑的滚动效果
// GestureBinding.instance?.resamplingEnabled = true;
......@@ -69,8 +69,7 @@ Future<void> main() async {
handleError(() => runApp(MyApp()));
/// 隐藏状态栏。为启动页、引导页设置。完成后修改回显示状态栏。
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: [SystemUiOverlay.bottom]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom]);
// TODO(weilu): 启动体验不佳。状态栏、导航栏在冷启动开始的一瞬间为黑色,且无法通过隐藏、修改颜色等方式进行处理。。。
// 相关问题跟踪:https://github.com/flutter/flutter/issues/73351
if (Platform.isAndroid) {
......@@ -133,8 +132,7 @@ class MyApp extends StatelessWidget {
}
quickActions.setShortcutItems(<ShortcutItem>[
const ShortcutItem(
type: 'demo', localizedTitle: '发一言', icon: 'flutter_dash_black'),
const ShortcutItem(type: 'demo', localizedTitle: '发一言', icon: 'flutter_dash_black'),
]);
}
}
......@@ -149,8 +147,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => MembershipViewProvider())
],
child: Consumer2<ThemeProvider, LocaleProvider>(
builder:
(_, ThemeProvider provider, LocaleProvider localeProvider, __) {
builder: (_, ThemeProvider provider, LocaleProvider localeProvider, __) {
return _buildMaterialApp(provider, localeProvider);
},
),
......@@ -159,15 +156,13 @@ class MyApp extends StatelessWidget {
/// Toast 配置
return OKToast(
backgroundColor: Colors.black54,
textPadding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
textPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0),
radius: 20.0,
position: ToastPosition.bottom,
child: app);
}
Widget _buildMaterialApp(
ThemeProvider provider, LocaleProvider localeProvider) {
Widget _buildMaterialApp(ThemeProvider provider, LocaleProvider localeProvider) {
return MaterialApp(
title: '一言',
// showPerformanceOverlay: true, //显示性能标签
......@@ -185,7 +180,7 @@ class MyApp extends StatelessWidget {
supportedLocales: ParlandoLocalizations.supportedLocales,
locale: localeProvider.locale,
navigatorKey: navigatorKey,
builder: (BuildContext context, Widget? child) {
builder: EasyLoading.init(builder: (context, child) {
/// 仅针对安卓
if (Device.isAndroid) {
/// 切换深色模式会触发此方法,这里设置导航栏颜色
......@@ -197,7 +192,7 @@ class MyApp extends StatelessWidget {
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0),
child: child!,
);
},
}),
/// 因为使用了fluro,这里设置主要针对Web
onUnknownRoute: (_) {
......
......@@ -2,11 +2,15 @@ import 'dart:async';
import 'dart:ui';
import 'package:Parlando/apis/api_response.dart';
import 'package:Parlando/base/base_state.dart';
import 'package:Parlando/extension/widget_ext.dart';
import 'package:Parlando/login/login_router.dart';
import 'package:Parlando/membership/models/membership_entity.dart';
import 'package:Parlando/membership/view_models/membership_view_model.dart';
import 'package:Parlando/payment/payment_sdk.dart';
import 'package:Parlando/payment/payment_service.dart';
import 'package:Parlando/res/constant.dart';
import 'package:Parlando/widgets/my_button.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flustars/flustars.dart';
import 'package:flutter/material.dart';
......@@ -15,6 +19,7 @@ import 'package:Parlando/routers/fluro_navigator.dart';
import 'package:Parlando/extension/int_extension.dart';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
import 'package:getwidget/getwidget.dart';
import 'package:in_app_purchase_platform_interface/src/types/product_details.dart';
import 'package:provider/provider.dart';
class MembershipPage extends StatefulWidget {
......@@ -24,119 +29,112 @@ class MembershipPage extends StatefulWidget {
MembershipPageState createState() => MembershipPageState();
}
class MembershipPageState extends State<MembershipPage>
with WidgetsBindingObserver {
bool _isLoading = false;
class MembershipPageState extends BaseState<MembershipPage> with WidgetsBindingObserver {
final List<Widget> _productWidgets = <Widget>[];
Function? connectionCallBack;
ApiResponse? apiResponse;
@override
void initState() {
super.initState();
PaymentSdk.instance.initState();
PaymentSdk.instance.queryProducts().then((value) {
for (var element in value) {
_productWidgets.add(buildBuyItem(element));
}
setState(() {});
});
if (SpUtil.containsKey(Constant.userToken)!) {
Provider.of<MembershipViewProvider>(context, listen: false)
.setSelectedMembership(null);
Provider.of<MembershipViewProvider>(context, listen: false)
.fetchMembershipData('0');
PaymentService.instance.initConnection();
Provider.of<MembershipViewProvider>(context, listen: false).setSelectedMembership(null);
Provider.of<MembershipViewProvider>(context, listen: false).fetchMembershipData('0');
} else {
NavigatorUtils.push(context, LoginRouter.loginPage, replace: true);
}
}
@override
Widget build(BuildContext context) {
ApiResponse apiResponse =
Provider.of<MembershipViewProvider>(context).response;
switch (apiResponse.status) {
case Status.LOADING:
return const Center(child: CircularProgressIndicator());
case Status.COMPLETED:
MembershipData mb = apiResponse.data as MembershipData;
return SafeArea(
child: Scaffold(
backgroundColor: Colors.black,
void onFirstBuildBody(BuildContext context) {
super.onFirstBuildBody(context);
showLoading();
}
@override
Widget buildBody(BuildContext context) {
apiResponse = Provider.of<MembershipViewProvider>(context).response;
if (apiResponse?.status != Status.LOADING) {
hideLoading();
}
var url = "https://api.parlando.ink/storage/images/274d864a62d277b2a29c4db39f92d591.png";
return Scaffold(
body: Container(
alignment: Alignment.topCenter,
decoration: BoxDecoration(
image: DecorationImage(
image: CachedNetworkImageProvider(mb.bgImages!),
fit: BoxFit.fill,
),
image: DecorationImage(image: CachedNetworkImageProvider(url), fit: BoxFit.fill),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
alignment: Alignment.centerLeft,
child: IconButton(
onPressed: () {
NavigatorUtils.goBack(context);
},
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
initBackBar(),
Spacer(),
// buildCompleteWidget(),
],
),
),
const Spacer(),
Container(
margin: EdgeInsets.symmetric(
vertical: 60.px, horizontal: 20.px),
);
}
Widget buildLoading() {
return const Center(child: CircularProgressIndicator());
}
Widget buildCompleteWidget() {
MembershipData? mb = apiResponse?.data;
return Container(
height: MediaQuery.of(context).size.height / 2,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.1),
border: Border.all(
color: Colors.grey.shade50,
width: 0.5,
), // 边色与边宽度
border: Border.all(color: Colors.grey.shade50, width: 0.5), // 边色与边宽度
),
margin: const EdgeInsets.all(20),
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 10.0,
sigmaY: 10.0,
),
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade200.withOpacity(0.1),
),
decoration: BoxDecoration(color: Colors.grey.shade200.withOpacity(0.1)),
child: Padding(
padding: EdgeInsets.all(10.px),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
mb.title!,
style: TextStyle(
fontSize: 18.px,
color: Colors.white,
child: buildMemberContent(mb),
),
),
Gaps.vGap24,
// TODO 如果会员则显示会员详情
SizedBox(
width: double.infinity,
height: 100,
child: _productWidgets.isEmpty
? Column(
children: _productWidgets,
)
: const GFLoader(),
),
Gaps.vGap24,
Text(
mb.intro!,
style: TextStyle(
fontSize: 14.px,
color: Colors.white,
),
),
Gaps.vGap10,
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
);
}
@override
void dispose() {
PaymentSdk.instance.dispose();
super.dispose();
}
Widget buildBuyItem(ProductDetails element) {
var style = const TextStyle(color: Colors.white);
var button = Text("购买", style: style).paddingLeftRight(15).paddingTopBottom(8).click(() {
PaymentSdk.instance.buy(element);
}, color: Colors.blue).round(radius: 10);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(element.price, style: style),
button,
],
).paddingLeftRight(10).paddingTopBottom(5);
}
Widget initOtherEntrance() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
......@@ -144,82 +142,53 @@ class MembershipPageState extends State<MembershipPage>
onPressed: () {},
child: Text(
"服务协议",
style: TextStyle(
fontSize: 14.px,
color: Colors.white,
style: TextStyle(fontSize: 14.px, color: Colors.white),
),
),
),
Container(
width: 0.6,
height: 15.0,
color: Colours.line,
),
Container(width: 0.6, height: 15.0, color: Colours.line),
TextButton(
onPressed: () {},
child: Text(
"隐私政策",
style: TextStyle(
fontSize: 14.px,
color: Colors.white,
),
style: TextStyle(fontSize: 14.px, color: Colors.white),
),
),
Container(
width: 0.6,
height: 15.0,
color: Colours.line,
),
Container(width: 0.6, height: 15.0, color: Colours.line),
TextButton(
onPressed: () {},
child: Text(
"恢复购买",
style: TextStyle(
fontSize: 14.px,
color: Colors.white,
),
),
),
],
),
],
),
),
),
),
style: TextStyle(fontSize: 14.px, color: Colors.white),
),
),
],
),
),
),
);
case Status.ERROR:
return Center(
child: Text('暂时无法获取数据,请稍候再试!${apiResponse.message}'),
);
case Status.INITIAL:
default:
return const Center(
child: Text('正在获取数据....'),
);
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {}
@override
void dispose() {
PaymentService.instance.dispose();
super.dispose();
buildMemberContent(MembershipData? mb) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(mb?.title ?? "一言会员", style: TextStyle(fontSize: 18.px, color: Colors.white)),
Gaps.vGap24,
_productWidgets.isNotEmpty ? Column(children: _productWidgets) : const GFLoader(),
Gaps.vGap24,
Text(mb?.intro ?? "一言介绍", style: TextStyle(fontSize: 14.px, color: Colors.white)),
Gaps.vGap10,
initOtherEntrance(),
],
);
}
void _renderInApps() async {
List<IAPItem> items = await PaymentService.instance.products;
for (IAPItem item in items) {
_productWidgets.add(Text(item.title!));
}
setState(() {});
initBackBar() {
return Container(
alignment: Alignment.centerLeft,
child: IconButton(
onPressed: () {
NavigatorUtils.goBack(context);
},
icon: const Icon(Icons.arrow_back_ios, color: Colors.white),
),
);
}
}
......
......@@ -122,7 +122,7 @@ class LoggingInterceptor extends Interceptor {
Log.e('ResponseCode: ${response.statusCode}');
}
// 输出结果
Log.json(response.data.toString());
// Log.json(response.data.toString());
Log.d('----------End: $duration 毫秒----------');
super.onResponse(response, handler);
}
......
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
class PaymentSdk {
PaymentSdk._privateConstructor();
static final PaymentSdk _instance = PaymentSdk._privateConstructor();
static PaymentSdk get instance {
return _instance;
}
static const Set<String> _kIds = <String>{'yearly_yiyan_vip', 'monthly_yiyan_vip', 'yearly-default'};
StreamSubscription<List<PurchaseDetails>>? _subscription;
List<ProductDetails> products = [];
initState() {
final Stream<List<PurchaseDetails>> purchaseUpdated = InAppPurchase.instance.purchaseStream;
_subscription = purchaseUpdated.listen((purchaseDetailsList) {
_listenToPurchaseUpdated(purchaseDetailsList);
}, onDone: () {
_subscription?.cancel();
}, onError: (error) {
// handle error here.
});
}
Future<List<ProductDetails>> queryProducts() async {
final bool available = await InAppPurchase.instance.isAvailable();
if (!available) {
print("####### isAvailable false");
return [];
}
final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails({
'yearly_yiyan_vip',
'monthly_yiyan_vip',
'yearly-default',
'test.yiyan.vip.1.month',
});
if (response.notFoundIDs.isNotEmpty) {
// Handle the error.
print("####### notFoundIDs");
} else {
}
print(response.productDetails.length);
return products = response.productDetails;
}
buy(ProductDetails details) {
final PurchaseParam purchaseParam = PurchaseParam(productDetails: details);
if (_isConsumable(details)) {
InAppPurchase.instance.buyConsumable(purchaseParam: purchaseParam);
} else {
InAppPurchase.instance.buyNonConsumable(purchaseParam: purchaseParam);
}
}
void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) async {
for (var purchaseDetails in purchaseDetailsList) {
if (purchaseDetails.status == PurchaseStatus.pending) {
// _showPendingUI();
} else {
if (purchaseDetails.status == PurchaseStatus.error) {
// _handleError(purchaseDetails.error!);
} else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) {
bool valid = await _verifyPurchase(purchaseDetails);
if (valid) {
_deliverProduct(purchaseDetails);
} else {
_handleInvalidPurchase(purchaseDetails);
}
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
}
}
}
}
_verifyPurchase(PurchaseDetails purchaseDetails) async {
return true;
}
void _deliverProduct(PurchaseDetails purchaseDetails) {}
void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {}
bool _isConsumable(ProductDetails details) {
return true;
}
void dispose() {
_subscription?.cancel();
}
}
......@@ -26,7 +26,7 @@ class PaymentService {
late StreamSubscription<PurchaseResult?> _purchaseErrorSubscription;
/// List of product ids you want to fetch
final List<String> _productIds = ['test.yiyan.vip.1.month'];
final List<String> _productIds = ['yearly_yiyan_vip', 'monthly_yiyan_vip'];
/// All available products will be store in this list
late List<IAPItem> _products;
......@@ -36,12 +36,12 @@ class PaymentService {
/// view of the app will subscribe to this to get notified
/// when premium status of the user changes
final ObserverList<Function> _proStatusChangedListeners =
ObserverList<Function>();
final ObserverList<Function> _proStatusChangedListeners = ObserverList<Function>();
/// view of the app will subscribe to this to get errors of the purchase
final ObserverList<Function(String)> _errorListeners =
ObserverList<Function(String)>();
final ObserverList<Function(String)> _errorListeners = ObserverList<Function(String)>();
final ObserverList<Function> _connectListeners = ObserverList<Function>();
/// logged in user's premium status
bool _isProUser = false;
......@@ -68,6 +68,20 @@ class PaymentService {
_errorListeners.remove(callback);
}
addConnectListener(Function? callback) {
if (callback == null) {
return;
}
_connectListeners.add(callback);
}
removeConnectListener(Function? callback) {
if (callback == null) {
return;
}
_connectListeners.remove(callback);
}
/// Call this method to notify all the subsctibers of _proStatusChangedListeners
void _callProStatusChangedListeners() {
for (var callback in _proStatusChangedListeners) {
......@@ -84,18 +98,19 @@ class PaymentService {
/// Call this method at the startup of you app to initialize connection
/// with billing server and get all the necessary data
void initConnection() {
var result = FlutterInappPurchase.instance.initialize();
void initConnection() async {
var result = await FlutterInappPurchase.instance.initialize();
print("___________________________");
print("result:$result");
_connectionSubscription =
FlutterInappPurchase.connectionUpdated.listen((connected) {});
_connectionSubscription = FlutterInappPurchase.connectionUpdated.listen((connected) {
for (var value in _connectListeners) {
value.call();
}
});
_purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen(_handlePurchaseUpdate);
_purchaseUpdatedSubscription = FlutterInappPurchase.purchaseUpdated.listen(_handlePurchaseUpdate);
_purchaseErrorSubscription =
FlutterInappPurchase.purchaseError.listen(_handlePurchaseError);
_purchaseErrorSubscription = FlutterInappPurchase.purchaseError.listen(_handlePurchaseError);
_getItems();
_getPastPurchases();
......@@ -191,14 +206,14 @@ class PaymentService {
}
Future<void> _getItems() async {
List<IAPItem> items =
await FlutterInappPurchase.instance.getSubscriptions(_productIds);
List<IAPItem> items = await FlutterInappPurchase.instance.getSubscriptions(_productIds);
print("############${items.length}");
_products = [];
for (var item in items) {
_products.add(item);
}
print("############");
print(_products);
print("############${_products}");
}
void _getPastPurchases() async {
......@@ -206,8 +221,7 @@ class PaymentService {
if (Platform.isIOS) {
return;
}
List<PurchasedItem>? purchasedItems =
await FlutterInappPurchase.instance.getAvailablePurchases();
List<PurchasedItem>? purchasedItems = await FlutterInappPurchase.instance.getAvailablePurchases();
for (var purchasedItem in purchasedItems!) {
bool isValid = false;
......@@ -236,8 +250,7 @@ class PaymentService {
Future<void> buyProduct(IAPItem item) async {
try {
await FlutterInappPurchase.instance
.requestSubscription(item.productId.toString());
await FlutterInappPurchase.instance.requestSubscription(item.productId.toString());
} catch (error) {
Toast.show("购买失败!");
}
......
......@@ -417,6 +417,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.2"
flutter_easyloading:
dependency: "direct main"
description:
name: flutter_easyloading
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.5"
flutter_facebook_auth:
dependency: "direct main"
description:
......@@ -710,6 +717,34 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.6.2"
in_app_purchase:
dependency: "direct main"
description:
name: in_app_purchase
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.8"
in_app_purchase_android:
dependency: transitive
description:
name: in_app_purchase_android
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.3+6"
in_app_purchase_platform_interface:
dependency: transitive
description:
name: in_app_purchase_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.2"
in_app_purchase_storekit:
dependency: transitive
description:
name: in_app_purchase_storekit
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.3"
integration_test:
dependency: "direct dev"
description: flutter
......
......@@ -101,6 +101,8 @@ dependencies:
# A Dart timer that can be paused, resumed and reset.
pausable_timer: ^1.0.0+3
flutter_easyloading: ^3.0.0
email_validator: ^2.0.1
getwidget: ^2.0.5
......@@ -114,7 +116,10 @@ dependencies:
animated_radial_menu:
path: plugins/animated_radial
# 非官方库 暂时不删
flutter_inapp_purchase: ^5.3.0
# Flutter官方支付支持库
in_app_purchase: ^3.0.8
jpush_flutter: ^2.2.9
share_plus: ^4.0.10
......@@ -125,6 +130,7 @@ dependencies:
google_fonts: ^3.0.1
wakelock: ^0.6.1+2
location: ^4.4.0
# GoogleMap支持库
google_maps_flutter: ^2.2.1
http: ^0.13.5
......