Reason Pun

Merge remote-tracking branch 'origin/dev_reason_v1.0' into dev_reason_v1.0

# Conflicts:
#	android/app/build.gradle
#	pubspec.lock
......@@ -32,8 +32,8 @@ if (keystorePropertiesFile.exists()) {
}
android {
compileSdkVersion 33
buildToolsVersion "33"
compileSdkVersion 32
buildToolsVersion "32"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
......
......@@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
<string>11.0</string>
</dict>
</plist>
......
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
platform :ios, '10.0'
platform :ios, '12.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
......
This diff is collapsed. Click to expand it.
......@@ -363,7 +363,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
......@@ -380,14 +380,14 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = FLS6Y2LS7Z;
DEVELOPMENT_TEAM = 5PX6JTCZ6G;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = pub.yiyan.parlando.Parlando;
PRODUCT_BUNDLE_IDENTIFIER = ink.parlando.parlando;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
......@@ -442,7 +442,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
......@@ -491,7 +491,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
......@@ -509,14 +509,14 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = FLS6Y2LS7Z;
DEVELOPMENT_TEAM = 5PX6JTCZ6G;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = pub.yiyan.parlando.Parlando;
PRODUCT_BUNDLE_IDENTIFIER = ink.parlando.parlando;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
......@@ -533,14 +533,14 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = FLS6Y2LS7Z;
DEVELOPMENT_TEAM = 5PX6JTCZ6G;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = pub.yiyan.parlando.Parlando;
PRODUCT_BUNDLE_IDENTIFIER = ink.parlando.parlando;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
......
......@@ -2,12 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSLocalNetworkUsageDescription</key>
<string>Allow flutter app</string>
<key>NSBonjourServices</key>
<array>
<string>_dartobservatory._tcp</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
......@@ -35,6 +29,12 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_dartobservatory._tcp</string>
</array>
<key>NSLocalNetworkUsageDescription</key>
<string>Allow flutter app</string>
<key>NSMicrophoneUsageDescription</key>
<string>打开话筒</string>
<key>UILaunchStoryboardName</key>
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>一言临境</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Parlando</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSMicrophoneUsageDescription</key>
<string>打开话筒</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
import 'dart:convert';
import 'package:Parlando/apis/api_response.dart';
import 'package:Parlando/net/dio_utils.dart';
import 'package:common_utils/common_utils.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class BaseApi {
Future<Response<T>> post<T>(
String path, {
data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
ProgressCallback? onSendProgress,
ProgressCallback? onReceiveProgress,
}) {
return _getDio().post<T>(
path,
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onSendProgress: onSendProgress,
onReceiveProgress: onReceiveProgress,
);
}
Dio _getDio() {
return DioUtils.instance.dio;
}
}
import 'dart:convert';
import 'package:Parlando/apis/api_base.dart';
import 'package:Parlando/apis/api_response.dart';
import 'package:common_utils/common_utils.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
class OrderApi extends BaseApi {
OrderApi._privateConstructor();
static final OrderApi _instance = OrderApi._privateConstructor();
static OrderApi get request {
return _instance;
}
Future<dynamic> createOrder(String productId) {
var data = {"goods_id": productId};
return post("order", data: data).then((value) {
if (TextUtil.isEmpty(value.data)) {
return {};
}
return json.decode(value.data);
});
}
Future<dynamic> verifyOrder(String orderId, String type, String token, {Map<String, dynamic>? others}) {
var data = {
"order_sn": orderId,
"pay_type": type,
"token": token,
"others": others ?? {},
};
return post("pay", data: data).then((value) {
if (TextUtil.isEmpty(value.data)) {
return {};
}
return value.data;
});
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
abstract class BaseState<T extends StatefulWidget> extends State<T> {
bool _isFirstBuild = true;
List<Timer> delayTimers = [];
@override
Widget build(BuildContext context) {
if (_isFirstBuild) {
onFirstBuildBody(context);
_isFirstBuild = false;
}
return buildBody(context);
}
postDelay(VoidCallback fn, int delayTime) {
var delayTimer = Timer(Duration(milliseconds: delayTime), fn);
delayTimers.add(delayTimer);
}
Widget buildBody(BuildContext context);
void onFirstBuildBody(BuildContext context) {}
showLoading({String text = 'Loading...'}) {
EasyLoading.show(status: text);
}
hideLoading() {
EasyLoading.dismiss();
}
toast(String text) {
EasyLoading.showToast(text);
}
Widget buildLoading() {
return const Center(child: CircularProgressIndicator());
}
@override
void dispose() {
super.dispose();
for (var element in delayTimers) {
element.cancel();
}
hideLoading();
}
}
import 'package:flutter/material.dart';
extension WidgetExt on Widget {
Expanded expanded({int flex = 1}) {
return Expanded(flex: flex, child: this);
}
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),
),
);
}
}
......@@ -68,6 +68,10 @@ MembershipData $MembershipDataFromJson(Map<String, dynamic> json) {
if (videoCover != null) {
membershipData.videoCover = videoCover;
}
final String? expireVipAt = jsonConvert.convert<String>(json['expire_vip_time']);
if (videoCover != null) {
membershipData.expireVipAt = expireVipAt;
}
final String? terminal = jsonConvert.convert<String>(json['terminal']);
if (terminal != null) {
membershipData.terminal = terminal;
......@@ -105,6 +109,7 @@ Map<String, dynamic> $MembershipDataToJson(MembershipData entity) {
data['bg_images'] = entity.bgImages;
data['video_url'] = entity.videoUrl;
data['video_cover'] = entity.videoCover;
data['expire_vip_time'] = entity.expireVipAt;
data['terminal'] = entity.terminal;
data['state'] = entity.state;
data['created_at'] = entity.createdAt;
......@@ -116,17 +121,19 @@ Map<String, dynamic> $MembershipDataToJson(MembershipData entity) {
MembershipDataGoodsList $MembershipDataGoodsListFromJson(
Map<String, dynamic> json) {
final MembershipDataGoodsList membershipDataGoodsList =
MembershipDataGoodsList();
final MembershipDataGoodsList membershipDataGoodsList = MembershipDataGoodsList();
final int? id = jsonConvert.convert<int>(json['id']);
if (id != null) {
membershipDataGoodsList.id = id;
}
final String? membershipId =
jsonConvert.convert<String>(json['membership_id']);
final String? membershipId = jsonConvert.convert<String>(json['membership_id']);
if (membershipId != null) {
membershipDataGoodsList.membershipId = membershipId;
}
final String? iapId = jsonConvert.convert<String>(json['iap_id']);
if (iapId != null) {
membershipDataGoodsList.iapId = iapId;
}
final String? name = jsonConvert.convert<String>(json['name']);
if (name != null) {
membershipDataGoodsList.name = name;
......@@ -192,6 +199,7 @@ Map<String, dynamic> $MembershipDataGoodsListToJson(
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = entity.id;
data['membership_id'] = entity.membershipId;
data['iap_id'] = entity.iapId;
data['name'] = entity.name;
data['price'] = entity.price;
data['line_price'] = entity.linePrice;
......
......@@ -10,6 +10,10 @@ import 'package:Parlando/widgets/my_button.dart';
import 'package:Parlando/widgets/my_scroll_view.dart';
import 'package:Parlando/extension/int_extension.dart';
import 'package:getwidget/components/loader/gf_loader.dart';
import '../../net/dio_utils.dart';
import '../../net/http_api.dart';
class UpdatePasswordPage extends StatefulWidget {
const UpdatePasswordPage({Key? key}) : super(key: key);
......@@ -26,6 +30,7 @@ class _UpdatePasswordPageState extends State<UpdatePasswordPage>
final FocusNode _nodeText1 = FocusNode();
final FocusNode _nodeText2 = FocusNode();
bool _clickable = false;
bool _isLoading = false;
@override
Map<ChangeNotifier, List<VoidCallback>?>? changeNotifier() {
......@@ -56,8 +61,25 @@ class _UpdatePasswordPageState extends State<UpdatePasswordPage>
}
void _confirm() {
Toast.show('修改成功!');
NavigatorUtils.goBack(context);
_isLoading = true;
Map<String, String> params = <String, String>{
"password": _newPwdController.text,
"password_confirmation": _newPwdController.text,
};
DioUtils.instance.asyncRequestNetwork(
Method.post,
HttpApi.changePassword,
params: params,
onSuccess: (data) {
_isLoading = false;
NavigatorUtils.goBack(context);
},
onError: (code, msg) {
_isLoading = false;
Toast.show(msg.toString());
setState(() {});
},
);
}
@override
......@@ -76,7 +98,7 @@ class _UpdatePasswordPageState extends State<UpdatePasswordPage>
),
Gaps.vGap8,
Text(
'设置账号 15000000000',
' ',
style: Theme.of(context)
.textTheme
.subtitle2
......@@ -102,7 +124,10 @@ class _UpdatePasswordPageState extends State<UpdatePasswordPage>
MyButton(
onPressed: _clickable ? _confirm : null,
text: '确认',
)
),
Container(
child: _isLoading ? const GFLoader() : null,
),
],
),
);
......
......@@ -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: (_) {
......@@ -236,4 +231,4 @@ Future<bool> requestLocationPermission() async {
return false;
}
}
}
\ No newline at end of file
}
......
......@@ -42,6 +42,8 @@ class MembershipData {
String? createdAt;
@JSONField(name: "updated_at")
String? updatedAt;
@JSONField(name: "expire_vip_time")
String? expireVipAt;
@JSONField(name: "is_vip")
int? isVip;
@JSONField(name: "goods_list")
......@@ -65,6 +67,8 @@ class MembershipDataGoodsList {
int? id;
@JSONField(name: "membership_id")
String? membershipId;
@JSONField(name: "iap_id")
String? iapId;
String? name;
String? price;
@JSONField(name: "line_price")
......
This diff is collapsed. Click to expand it.
class NearbyPlacesResponse {
List<Results>? results;
String? status;
NearbyPlacesResponse({this.results, this.status});
NearbyPlacesResponse.fromJson(Map<String, dynamic> json) {
if (json['results'] != null) {
results = <Results>[];
json['results'].forEach((v) {
results!.add(Results.fromJson(v));
});
}
status = json['status'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
if (this.results != null) {
data['results'] = this.results!.map((v) => v.toJson()).toList();
}
data['status'] = this.status;
return data;
}
}
class Results {
Geometry? geometry;
String? icon;
String? iconBackgroundColor;
String? iconMaskBaseUri;
String? name;
List<Photos>? photos;
String? placeId;
String? reference;
String? scope;
List<String>? types;
String? vicinity;
String? businessStatus;
OpeningHours? openingHours;
PlusCode? plusCode;
dynamic rating;
int? userRatingsTotal;
bool isSelect = false;
Results(
{this.geometry,
this.icon,
this.iconBackgroundColor,
this.iconMaskBaseUri,
this.name,
this.photos,
this.placeId,
this.reference,
this.scope,
this.types,
this.vicinity,
this.businessStatus,
this.openingHours,
this.plusCode,
this.rating,
this.userRatingsTotal,
this.isSelect = false});
Results.fromJson(Map<String, dynamic> json) {
geometry = json['geometry'] != null ? Geometry.fromJson(json['geometry']) : null;
icon = json['icon'];
iconBackgroundColor = json['icon_background_color'];
iconMaskBaseUri = json['icon_mask_base_uri'];
name = json['name'];
if (json['photos'] != null) {
photos = <Photos>[];
json['photos'].forEach((v) {
photos!.add(Photos.fromJson(v));
});
}
placeId = json['place_id'];
reference = json['reference'];
scope = json['scope'];
types = json['types'].cast<String>();
vicinity = json['vicinity'];
businessStatus = json['business_status'];
openingHours = json['opening_hours'] != null ? OpeningHours.fromJson(json['opening_hours']) : null;
plusCode = json['plus_code'] != null ? PlusCode.fromJson(json['plus_code']) : null;
rating = json['rating'];
userRatingsTotal = json['user_ratings_total'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
if (this.geometry != null) {
data['geometry'] = this.geometry!.toJson();
}
data['icon'] = this.icon;
data['icon_background_color'] = this.iconBackgroundColor;
data['icon_mask_base_uri'] = this.iconMaskBaseUri;
data['name'] = this.name;
if (this.photos != null) {
data['photos'] = this.photos!.map((v) => v.toJson()).toList();
}
data['place_id'] = this.placeId;
data['reference'] = this.reference;
data['scope'] = this.scope;
data['types'] = this.types;
data['vicinity'] = this.vicinity;
data['business_status'] = this.businessStatus;
if (this.openingHours != null) {
data['opening_hours'] = this.openingHours!.toJson();
}
if (this.plusCode != null) {
data['plus_code'] = this.plusCode!.toJson();
}
data['rating'] = this.rating;
data['user_ratings_total'] = this.userRatingsTotal;
return data;
}
}
class Geometry {
Location? location;
Viewport? viewport;
Geometry({this.location, this.viewport});
Geometry.fromJson(Map<String, dynamic> json) {
location = json['location'] != null ? Location.fromJson(json['location']) : null;
viewport = json['viewport'] != null ? Viewport.fromJson(json['viewport']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
if (this.location != null) {
data['location'] = this.location!.toJson();
}
if (this.viewport != null) {
data['viewport'] = this.viewport!.toJson();
}
return data;
}
}
class Location {
double? lat;
double? lng;
Location({this.lat, this.lng});
Location.fromJson(Map<String, dynamic> json) {
lat = json['lat'];
lng = json['lng'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['lat'] = this.lat;
data['lng'] = this.lng;
return data;
}
}
class Viewport {
Location? northeast;
Location? southwest;
Viewport({this.northeast, this.southwest});
Viewport.fromJson(Map<String, dynamic> json) {
northeast = json['northeast'] != null ? Location.fromJson(json['northeast']) : null;
southwest = json['southwest'] != null ? Location.fromJson(json['southwest']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
if (this.northeast != null) {
data['northeast'] = this.northeast!.toJson();
}
if (this.southwest != null) {
data['southwest'] = this.southwest!.toJson();
}
return data;
}
}
class Photos {
int? height;
List<String>? htmlAttributions;
String? photoReference;
int? width;
Photos({this.height, this.htmlAttributions, this.photoReference, this.width});
Photos.fromJson(Map<String, dynamic> json) {
height = json['height'];
htmlAttributions = json['html_attributions'].cast<String>();
photoReference = json['photo_reference'];
width = json['width'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['height'] = this.height;
data['html_attributions'] = this.htmlAttributions;
data['photo_reference'] = this.photoReference;
data['width'] = this.width;
return data;
}
}
class OpeningHours {
bool? openNow;
OpeningHours({this.openNow});
OpeningHours.fromJson(Map<String, dynamic> json) {
openNow = json['open_now'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['open_now'] = this.openNow;
return data;
}
}
class PlusCode {
String? compoundCode;
String? globalCode;
PlusCode({this.compoundCode, this.globalCode});
PlusCode.fromJson(Map<String, dynamic> json) {
compoundCode = json['compound_code'];
globalCode = json['global_code'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['compound_code'] = this.compoundCode;
data['global_code'] = this.globalCode;
return data;
}
}
class PoiSearch {
PoiSearch({
required this.cityCode,
required this.cityName,
required this.provinceName,
required this.title,
required this.adName,
required this.provinceCode,
required this.latitude,
required this.longitude,
});
PoiSearch.fromJsonMap(Map<String, dynamic> map)
: cityCode = map['cityCode'] as String,
cityName = map['cityName'] as String,
provinceName = map['provinceName'] as String,
title = map['title'] as String,
adName = map['adName'] as String,
provinceCode = map['provinceCode'] as String,
latitude = map['latitude'] as String,
longitude = map['longitude'] as String;
String cityCode;
String cityName;
String provinceName;
String title;
String adName;
String provinceCode;
String latitude;
String longitude;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['cityCode'] = cityCode;
data['cityName'] = cityName;
data['provinceName'] = provinceName;
data['title'] = title;
data['adName'] = adName;
data['provinceCode'] = provinceCode;
data['latitude'] = latitude;
data['longitude'] = longitude;
return data;
}
}
......@@ -7,6 +7,7 @@ class HttpApi {
static const String uploadVideo = 'upload/video';
static const String uploadImage = 'upload/image';
static const String immersive = 'immersive';
static const String changePassword = 'user/changePassword';
static const String avatar = 'avatar';
static const String user = 'user';
static const String membership = 'membership';
......
import 'dart:async';
import 'package:Parlando/apis/api_order.dart';
import 'package:Parlando/membership/models/membership_entity.dart';
import 'package:common_utils/common_utils.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_android/in_app_purchase_android.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
class PaymentSdk {
var currentOrder;
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'};
StreamSubscription<List<PurchaseDetails>>? _subscription;
List<ProductDetails> products = [];
Function? onPaySuccess;
Function? onPending;
Function? onFailed;
Function? onCancel;
initState({
Function? onPaySuccess,
Function? onPending,
Function? onFailed,
Function? onCancel,
}) {
this.onPaySuccess = onPaySuccess;
this.onPending = onPending;
this.onFailed = onFailed;
this.onCancel = onCancel;
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) {
return [];
}
final ProductDetailsResponse response = await InAppPurchase.instance.queryProductDetails(_kIds);
return products = response.productDetails;
}
buy(ProductDetails details, MembershipDataGoodsList e) {
OrderApi.request.createOrder(e.id.toString()).then((value) {
var orderId = value?['data']?['data']?['order_sn'];
if (TextUtil.isEmpty(orderId)) {
onFailed?.call();
return;
}
currentOrder = orderId;
final PurchaseParam purchaseParam = PurchaseParam(productDetails: details, applicationUserName: orderId);
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) {
onPending?.call();
} 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);
}
} else {
onCancel?.call();
}
if (purchaseDetails.pendingCompletePurchase) {
await InAppPurchase.instance.completePurchase(purchaseDetails);
}
}
}
}
_verifyPurchase(PurchaseDetails purchaseDetails) async {
return true;
}
void _deliverProduct(PurchaseDetails purchaseDetails) {
String type = "";
Map<String, dynamic> otherField = {};
if (purchaseDetails is GooglePlayPurchaseDetails) {
currentOrder = purchaseDetails.billingClientPurchase.obfuscatedAccountId;
type = "google";
otherField["google"] = {
"originalJson": purchaseDetails.billingClientPurchase.originalJson,
};
}
if (purchaseDetails is AppStorePurchaseDetails) {
type = "apple";
otherField["apple"] = {
"transactionIdentifier": purchaseDetails.skPaymentTransaction.transactionIdentifier,
"originalTransactionIdentifier":
purchaseDetails.skPaymentTransaction.originalTransaction?.transactionIdentifier ?? "",
};
}
var serverVerifyStr = purchaseDetails.verificationData.serverVerificationData;
var verifySource = purchaseDetails.verificationData.source;
otherField["source"] = verifySource;
OrderApi.request.verifyOrder(currentOrder, type, serverVerifyStr, others: otherField).then((value) {
if (value != null) {}
onPaySuccess?.call();
});
}
void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
onFailed?.call();
}
void _handleError(IAPError iapError) {
onFailed?.call();
}
bool _isConsumable(ProductDetails details) {
return false;
}
void restore() {
InAppPurchase.instance.restorePurchases();
}
void dispose() {
onPaySuccess = null;
onPending = null;
onFailed = null;
onCancel = null;
_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("购买失败!");
}
......
import 'package:Parlando/models/nearby_response.dart';
import 'package:Parlando/models/upload_entity.dart';
import 'package:Parlando/net/dio_utils.dart';
import 'package:Parlando/net/http_api.dart';
......@@ -109,12 +110,10 @@ class PoemPublishState extends State<PoemPublish> {
NavigatorUtils.pushResult(
context, PoemRouter.addressSelectPage, (result) {
setState(() {
// final BMFSuggestionInfo model =
// result as BMFSuggestionInfo;
// _longitude = model.location!.longitude.toString();
// _latitude = model.location!.latitude.toString();
// _address =
// '${model.city!} ${model.district!} ${model.address!}';
final Results model = result as Results;
_longitude = model.geometry!.location!.lng.toString();
_latitude = model.geometry!.location!.lat.toString();
_address = '${model.name} ${model.vicinity}';
});
});
},
......@@ -130,10 +129,20 @@ class PoemPublishState extends State<PoemPublish> {
size: 15.px,
),
Gaps.hGap5,
Text(
_address,
style: const TextStyle(color: Colors.black45),
),
Container(
padding: const EdgeInsets.fromLTRB(0, 0, 10, 10),
width: MediaQuery.of(context).size.width * 0.8,
alignment: Alignment.centerLeft,
child: Column(
children: <Widget>[
Text(
_address,
style: const TextStyle(color: Colors.black45),
textAlign: TextAlign.left,
),
],
),
)
],
),
),
......@@ -209,12 +218,12 @@ class PoemPublishState extends State<PoemPublish> {
Gaps.vGap10,
isUploading
? Padding(
padding: const EdgeInsets.all(20),
child: ValueListenableBuilder<double>(
builder: _buildWithValue,
valueListenable: _counter,
),
)
padding: const EdgeInsets.all(20),
child: ValueListenableBuilder<double>(
builder: _buildWithValue,
valueListenable: _counter,
),
)
: Container(),
],
),
......@@ -224,10 +233,10 @@ class PoemPublishState extends State<PoemPublish> {
),
isPublishing
? const Center(
child: CupertinoActivityIndicator(
radius: 16.0,
),
)
child: CupertinoActivityIndicator(
radius: 16.0,
),
)
: Container(),
],
),
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
......@@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+12
version: 1.0.0+16
environment:
sdk: ">=2.16.2 <3.0.0"
......@@ -101,11 +101,13 @@ 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
sign_in_with_apple: ^4.0.0
flutter_facebook_auth: ^4.3.4+2
flutter_facebook_auth: ^5.0.6
flutter_signin_button: ^2.0.0
twitter_login: ^4.2.3
......@@ -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,7 +130,9 @@ dependencies:
google_fonts: ^3.0.1
wakelock: ^0.6.1+2
location: ^4.4.0
google_maps_flutter: ^2.1.10
# GoogleMap支持库
google_maps_flutter: ^2.2.1
http: ^0.13.5
dependency_overrides:
decimal: 1.5.0
......