reason

Merge branch 'dev_chad_v1.0' into 'dev_reason_v1.0'

Google Map& Google 订阅支付



See merge request !1
......@@ -75,6 +75,9 @@ android {
release {
signingConfig signingConfigs.release
}
debug {
signingConfig signingConfigs.release
}
}
}
......
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);
});
}
}
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();
}
toast(String text) {
EasyLoading.showToast(text);
}
Widget buildLoading() {
return const Center(child: CircularProgressIndicator());
}
}
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),
),
);
}
}
......@@ -116,17 +116,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 +194,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;
......
......@@ -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
}
......
......@@ -65,6 +65,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.
......@@ -41,6 +41,7 @@ class Results {
PlusCode? plusCode;
dynamic rating;
int? userRatingsTotal;
bool isSelect = false;
Results(
{this.geometry,
......@@ -58,11 +59,11 @@ class Results {
this.openingHours,
this.plusCode,
this.rating,
this.userRatingsTotal});
this.userRatingsTotal,
this.isSelect = false});
Results.fromJson(Map<String, dynamic> json) {
geometry =
json['geometry'] != null ? Geometry.fromJson(json['geometry']) : null;
geometry = json['geometry'] != null ? Geometry.fromJson(json['geometry']) : null;
icon = json['icon'];
iconBackgroundColor = json['icon_background_color'];
iconMaskBaseUri = json['icon_mask_base_uri'];
......@@ -79,11 +80,8 @@ class Results {
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;
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'];
}
......@@ -125,10 +123,8 @@ class Geometry {
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;
location = json['location'] != null ? Location.fromJson(json['location']) : null;
viewport = json['viewport'] != null ? Viewport.fromJson(json['viewport']) : null;
}
Map<String, dynamic> toJson() {
......@@ -169,10 +165,8 @@ class Viewport {
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;
northeast = json['northeast'] != null ? Location.fromJson(json['northeast']) : null;
southwest = json['southwest'] != null ? Location.fromJson(json['southwest']) : null;
}
Map<String, dynamic> toJson() {
......
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';
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'};
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;
}
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) {
onPaySuccess?.call();
}
void _handleInvalidPurchase(PurchaseDetails purchaseDetails) {
onFailed?.call();
}
void _handleError(IAPError iapError) {
onFailed?.call();
}
bool _isConsumable(ProductDetails details) {
return true;
}
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 'dart:async';
import 'dart:convert';
import 'package:Parlando/extension/widget_ext.dart';
import 'package:Parlando/models/nearby_response.dart' as nearby;
import 'package:Parlando/routers/fluro_navigator.dart';
import 'package:Parlando/util/log_utils.dart';
import 'package:Parlando/util/toast_utils.dart';
import 'package:flutter/material.dart';
import 'package:Parlando/widgets/my_button.dart';
......@@ -19,26 +21,17 @@ class AddressSelectPage extends StatefulWidget {
}
class AddressSelectPageState extends State<AddressSelectPage> {
List<nearby.Results> _list = [];
int _index = 0;
List<nearby.Results> _nearByList = [];
final ScrollController _controller = ScrollController();
LatLng _center = const LatLng(45.521563, -122.677433);
LatLng? _center;
late GoogleMapController mapController;
bool isLoading = false;
int _markerIdCounter = 1;
bool isLoading = true;
Map<MarkerId, Marker> markers = <MarkerId, Marker>{};
late StreamSubscription _locationSubscription;
String radius = "1000";
String radiusMax = "1000";
String apiKey = "AIzaSyDQZsMULyO-UtiSht4_MFi1uHT4BIqasjw";
nearby.NearbyPlacesResponse nearbyPlacesResponse =
nearby.NearbyPlacesResponse();
nearby.NearbyPlacesResponse nearbyPlacesResponse = nearby.NearbyPlacesResponse();
@override
void dispose() {
_controller.dispose();
_locationSubscription.cancel();
super.dispose();
}
@override
void initState() {
......@@ -64,116 +57,168 @@ class AddressSelectPageState extends State<AddressSelectPage> {
return;
}
}
_locationSubscription =
location.onLocationChanged.listen((LocationData currentLocation) {
_center = LatLng(currentLocation.latitude!, currentLocation.longitude!);
});
var currentLocation = await location.getLocation();
_center = LatLng(currentLocation.latitude!, currentLocation.longitude!);
getNearbyPlaces("");
_goToCurrentCenter();
}
void getNearbyPlaces(String keyword) async {
String uri =
'${'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${_center.latitude},${_center.longitude}&radius=$radius'}&key=$apiKey&keyword=$keyword';
String host = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json';
String location = 'location=${_center?.latitude},${_center?.longitude}';
String radius = 'radius=$radiusMax';
String types = 'type=point_of_interest';
String uri = '${'$host?$location&$radius'}&key=$apiKey&keyword=$keyword&$types';
print(uri);
var url = Uri.parse(uri);
var response = await http.post(url);
nearbyPlacesResponse =
nearby.NearbyPlacesResponse.fromJson(jsonDecode(response.body));
_list = nearbyPlacesResponse.results!;
nearbyPlacesResponse = nearby.NearbyPlacesResponse.fromJson(jsonDecode(response.body));
_nearByList = nearbyPlacesResponse.results!;
if (_nearByList.isNotEmpty) {
selectItemLocation(_nearByList[0]);
}
setState(() {
isLoading = false;
});
buildMarkers();
}
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
final String markerIdVal = 'marker_id_$_markerIdCounter';
_markerIdCounter++;
final MarkerId markerId = MarkerId(markerIdVal);
final Marker marker = Marker(
markerId: markerId,
position: _center,
);
mapController.moveCamera(CameraUpdate.newLatLng(_center));
setState(() {
markers[markerId] = marker;
});
getNearbyPlaces("");
}
void _goToCurrentCenter() {
if (_center != null) {
mapController.moveCamera(CameraUpdate.newLatLng(_center!));
}
}
@override
Widget build(BuildContext context) {
var loaderView = const GFLoader().expanded(flex: 11);
Widget realList = ListView.separated(
controller: _controller,
itemCount: _nearByList.length,
separatorBuilder: (_, index) => const Divider(),
itemBuilder: (_, index) {
var item = _nearByList[index];
return _AddressItem(
isSelected: item.isSelect,
data: item,
onTap: () {
selectItemLocation(item);
},
);
},
).expanded(flex: 11);
if (_nearByList.isEmpty) {
realList = const Center(child: Text("没有找到任何地点")).expanded(flex: 11);
}
var listHolder = isLoading ? loaderView : realList;
var searchBar = SearchBar(
hintText: '搜索地址',
onPressed: (text) async {
isLoading = true;
_controller.animateTo(0.0, duration: const Duration(milliseconds: 10), curve: Curves.ease);
// 构造检索参数
getNearbyPlaces(text);
setState(() {
});
},
);
var map = GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(target: _center ?? const LatLng(45.521563, -122.677433), zoom: 16.0),
markers: Set<Marker>.of(markers.values),
myLocationEnabled: true,
myLocationButtonEnabled: true,
).expanded(flex: 9);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: SearchBar(
hintText: '搜索地址',
onPressed: (text) async {
isLoading = true;
_controller.animateTo(0.0,
duration: const Duration(milliseconds: 10), curve: Curves.ease);
_index = 0;
// 构造检索参数
getNearbyPlaces(text);
},
),
body: SafeArea(
child: Column(
children: <Widget>[
Expanded(
flex: 9,
child: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 16.0,
),
markers: Set<Marker>.of(markers.values),
),
),
Expanded(
flex: 11,
child: isLoading
? const GFLoader()
: ListView.separated(
controller: _controller,
itemCount: _list.length,
separatorBuilder: (_, index) => const Divider(),
itemBuilder: (_, index) {
return _AddressItem(
isSelected: _index == index,
date: _list[index],
onTap: () {
_index = index;
setState(() {});
},
);
},
),
),
MyButton(
onPressed: () {
if (_list.isEmpty) {
Toast.show('未选择地址!');
return;
}
NavigatorUtils.goBackWithParams(context, _list[_index]);
},
text: '确认选择地址',
)
],
),
),
appBar: searchBar,
body: Column(
children: <Widget>[map, listHolder, initButton()],
).safe(),
);
}
initButton() {
return MyButton(
onPressed: () {
var selected = _nearByList.where((element) => element.isSelect);
if (selected.isEmpty) {
Toast.show('未选择地址!');
return;
}
NavigatorUtils.goBackWithParams(context, selected.first);
},
text: '确认选择地址',
);
}
void buildMarkers() {
markers.clear();
for (var value in _nearByList) {
final MarkerId markerId = MarkerId(buildMarkerId(value));
final Marker marker = Marker(
icon: value.isSelect ? BitmapDescriptor.defaultMarker : BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue),
markerId: markerId,
position: buildMarkerLocation(value),
);
markers[markerId] = marker;
}
setState(() {});
}
String buildMarkerId(nearby.Results value) {
var lat = value.geometry?.location?.lat;
var lng = value.geometry?.location?.lng;
return "$lat-$lng";
}
LatLng buildMarkerLocation(nearby.Results value) {
var lat = value.geometry?.location?.lat;
var lng = value.geometry?.location?.lng;
return LatLng(lat ?? 0, lng ?? 0);
}
void selectItemLocation(nearby.Results item) {
for (var element in _nearByList) {
element.isSelect = false;
}
item.isSelect = true;
var lat = item.geometry?.location?.lat;
var lng = item.geometry?.location?.lng;
if (lat != null && lng != null) {
_center = LatLng(lat, lng);
}
_goToCurrentCenter();
setState(() {});
buildMarkers();
}
@override
void dispose() {
_controller.dispose();
_locationSubscription.cancel();
super.dispose();
}
}
class _AddressItem extends StatelessWidget {
const _AddressItem({
Key? key,
required this.date,
required this.data,
this.isSelected = false,
this.onTap,
}) : super(key: key);
final nearby.Results date;
final nearby.Results data;
final bool isSelected;
final GestureTapCallback? onTap;
......@@ -187,15 +232,8 @@ class _AddressItem extends StatelessWidget {
height: 50.0,
child: Row(
children: <Widget>[
Expanded(
child: Text(
'${date.name} ${date.vicinity}',
),
),
Visibility(
visible: isSelected,
child: const Icon(Icons.done, color: Colors.blue),
)
Text('${data.name}').expanded(),
Visibility(visible: isSelected, child: const Icon(Icons.done, color: Colors.blue))
],
),
),
......
......@@ -28,7 +28,7 @@ packages:
name: archive
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.3.0"
version: "3.1.11"
args:
dependency: transitive
description:
......@@ -42,7 +42,7 @@ packages:
name: async
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.9.0"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
......@@ -112,21 +112,21 @@ packages:
name: cached_network_image
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.2"
version: "3.2.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.0"
version: "1.0.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.2"
version: "1.0.1"
camera:
dependency: "direct main"
description:
......@@ -168,7 +168,14 @@ packages:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.1"
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
checked_yaml:
dependency: transitive
description:
......@@ -189,7 +196,7 @@ packages:
name: clock
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
version: "1.1.0"
code_builder:
dependency: transitive
description:
......@@ -224,7 +231,7 @@ packages:
name: coverage
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.5.0"
version: "1.2.0"
cross_file:
dependency: transitive
description:
......@@ -238,7 +245,7 @@ packages:
name: crypto
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
version: "3.0.1"
csslib:
dependency: transitive
description:
......@@ -336,7 +343,7 @@ packages:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
version: "1.3.0"
ffi:
dependency: transitive
description:
......@@ -410,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:
......@@ -463,7 +477,7 @@ packages:
name: flutter_native_splash
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.10+1"
version: "2.2.9"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
......@@ -703,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
......@@ -757,6 +799,13 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.1.0"
lint:
dependency: transitive
description:
name: lint
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
lints:
dependency: transitive
description:
......@@ -805,21 +854,21 @@ packages:
name: matcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.12"
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.5"
version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.0"
version: "1.7.0"
mime:
dependency: transitive
description:
......@@ -868,7 +917,7 @@ packages:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.2"
version: "1.8.1"
path_provider:
dependency: "direct main"
description:
......@@ -1272,7 +1321,7 @@ packages:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.9.0"
version: "1.8.2"
sp_util:
dependency: transitive
description:
......@@ -1293,7 +1342,7 @@ packages:
name: sqflite
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0+1"
version: "2.0.3+1"
sqflite_common:
dependency: transitive
description:
......@@ -1335,14 +1384,14 @@ packages:
name: string_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.1"
version: "1.1.0"
sync_http:
dependency: transitive
description:
name: sync_http
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.3.1"
version: "0.3.0"
synchronized:
dependency: transitive
description:
......@@ -1363,28 +1412,28 @@ packages:
name: term_glyph
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.1"
version: "1.2.0"
test:
dependency: "direct dev"
description:
name: test
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.21.4"
version: "1.21.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.12"
version: "0.4.9"
test_core:
dependency: transitive
description:
name: test_core
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.16"
version: "0.4.13"
timing:
dependency: transitive
description:
......@@ -1405,7 +1454,7 @@ packages:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
version: "1.3.0"
universal_io:
dependency: transitive
description:
......@@ -1538,7 +1587,7 @@ packages:
name: vm_service
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.0.0"
version: "8.2.2"
wakelock:
dependency: "direct main"
description:
......@@ -1659,5 +1708,5 @@ packages:
source: hosted
version: "3.1.1"
sdks:
dart: ">=2.18.0 <3.0.0"
flutter: ">=3.3.0"
dart: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"
......
......@@ -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,7 +130,8 @@ 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:
......