flutter

//
// Created with Android Studio.
// User: 三帆
// Date: 10/02/2019
// Time: 21:52
// email: sanfan.hx@alibaba-inc.com
// tartget:  xxx
//

import 'dart:async';
import 'dart:core';
import 'package:city_pickers/modal/base_citys.dart';
import 'package:city_pickers/modal/point.dart';
import 'package:city_pickers/modal/result.dart';
import 'package:city_pickers/src/show_types.dart';
import 'package:city_pickers/src/util.dart';
import 'package:flutter/material.dart';
import 'package:amap_flutter_location/amap_flutter_location.dart';
import 'package:amap_flutter_location/amap_location_option.dart';
import 'package:permission_handler/permission_handler.dart';

class FullPage extends StatefulWidget {
  final String? locationCode;
  final ShowType showType;
  final Map<String, String> provincesData;
  final Map<String, dynamic> citiesData;

  FullPage({
    this.locationCode,
    required this.showType,
    required this.provincesData,
    required this.citiesData,
  });

  @override
  _FullPageState createState() => _FullPageState();
}

// 界面状态
enum Status {
  Province,
  City,
  Area,
  Over,
}

class HistoryPageInfo {
  Status status;
  List<Point> itemList;

  HistoryPageInfo({required this.status, required this.itemList});
}

class _FullPageState extends State<FullPage> {
  /// list scroll control
  late ScrollController scrollController;

  /// provinces object [Point]
  late List<Point> provinces;

  /// cityTree modal ,for building tree that root is province
  late CityTree cityTree;

  /// page current statue, show p or a or c or over
  late Status pageStatus;

  /// show items maybe province city or area;

  late List<Point> itemList;

  /// body history, the max length is three
  List<HistoryPageInfo> _history = [];

  /// the target province user selected
  late Point targetProvince;

  /// the target city user selected
  Point? targetCity;

  /// the target area user selected
  Point? targetArea;

  @override
  void initState() {
    super.initState();

    scrollController = new ScrollController();
    provinces = new Provinces(metaInfo: widget.provincesData).provinces;
    cityTree = new CityTree(
        metaInfo: widget.citiesData, provincesInfo: widget.provincesData);
    itemList = provinces;
    pageStatus = Status.Province;
    try {
      _initLocation(widget.locationCode);
    } catch (e) {
      print('Exception details:\n 初始化地理位置信息失败, 请检查省分城市数据 \n $e');
    }


    requestPermission();

    ///设置Android和iOS的apiKey<br>
    ///key的申请请参考高德开放平台官网说明<br>
    ///Android: https://lbs.amap.com/api/android-location-sdk/guide/create-project/get-key
    ///iOS: https://lbs.amap.com/api/ios-location-sdk/guide/create-project/get-key
    // AMapFlutterLocation.setApiKey("f710757e921fdcdb74b7fc29695c589d", "f710757e921fdcdb74b7fc29695c589d");

    ///iOS 获取native精度类型
    // if (Platform.isIOS) {
    //   requestAccuracyAuthorization();
    // }

    ///注册定位结果监听
    _locationListener = _locationPlugin.onLocationChanged().listen((Map<String, Object> result) {
      setState(() {
        _locationResult = result;
      });

      //             _locationResult['province'] +
      //             _locationResult['city'] +
      //             _locationResult['district'] +
      //             _locationResult['street'];
      print("定位结果${_locationResult['city']}");
    });
  }

  Future<bool> back() {
    HistoryPageInfo? last = _history.length > 0 ? _history.last : null;
    if (last != null && mounted) {
      this.setState(() {
        pageStatus = last.status;
        itemList = last.itemList;
      });
      _history.removeLast();
      return Future<bool>.value(false);
    }
    return Future<bool>.value(true);
  }

  void _initLocation(String? locationCode) {
    int _locationCode;
    if (locationCode != null) {
      try {
        _locationCode = int.parse(locationCode);
      } catch (e) {
        print(ArgumentError(
            "The Argument locationCode must be valid like: '100000' but get '$locationCode' "));
        return;
      }

      targetProvince = cityTree.initTreeByCode(_locationCode);
      if (targetProvince.isNull) {
        targetProvince = cityTree.initTreeByCode(provinces.first.code!);
      }
      targetProvince.child.forEach((Point _city) {
        if (_city.code == _locationCode) {
          targetCity = _city;
          targetArea = _getTargetChildFirst(_city) ?? null;
        }
        _city.child.forEach((Point _area) {
          if (_area.code == _locationCode) {
            targetCity = _city;
            targetArea = _area;
          }
        });
      });
    } else {
      targetProvince =
          cityTree.initTreeByCode(int.parse(widget.provincesData.keys.first));
    }

    if (targetCity == null) {
      targetCity = _getTargetChildFirst(targetProvince);
    }
    if (targetArea == null) {
      targetArea = _getTargetChildFirst(targetCity!);
    }
  }

  Result _buildResult() {
    Result result = Result();
    ShowType showType = widget.showType;
    try {
      if (showType.contain(ShowType.p)) {
        result.provinceId = targetProvince.code.toString();
        result.provinceName = targetProvince.name;
      }
      if (showType.contain(ShowType.c)) {
        result.provinceId = targetProvince.code.toString();
        result.provinceName = targetProvince.name;
        result.cityId = targetCity?.code.toString();
        result.cityName = targetCity?.name;
      }
      if (showType.contain(ShowType.a)) {
        result.provinceId = targetProvince.code.toString();
        result.provinceName = targetProvince.name;
        result.cityId = targetCity?.code.toString();
        result.cityName = targetCity?.name;
        result.areaId = targetArea?.code.toString();
        result.areaName = targetArea?.name;
      }
    } catch (e) {
      print('Exception details:\n _buildResult error \n $e');
      // 此处兼容, 部分城市下无地区信息的情况
    }

    // 台湾异常数据. 需要过滤
    // if (result.provinceId == "710000") {
    //   result.cityId = null;
    //   result.cityName = null;
    //   result.areaId = null;
    //   result.areaName = null;
    // }
    return result;
  }

  Point? _getTargetChildFirst(Point target) {
    if (target == null) {
      return null;
    }
    if (target.child != null && target.child.isNotEmpty) {
      return target.child.first;
    }
    return null;
  }

  popHome() {
    Navigator.of(context).pop(_buildResult());
  }

  _onProvinceSelect(Point province) {
    this.setState(() {
      targetProvince = cityTree.initTree(province.code!);
    });
  }

  _onAreaSelect(Point area) {
    this.setState(() {
      targetArea = area;
    });
  }

  _onCitySelect(Point city) {
    this.setState(() {
      targetCity = city;
    });
  }

  int _getSelectedId() {
    int? selectId;
    switch (pageStatus) {
      case Status.Province:
        selectId = targetProvince.code;
        break;
      case Status.City:
        selectId = targetCity?.code;
        break;
      case Status.Area:
        selectId = targetArea?.code;
        break;
      case Status.Over:
        break;
    }
    return selectId ?? 0;
  }


  /// 所有选项的点击事件入口
  /// @param targetPoint 被点击对象的point对象
  _onItemSelect(Point targetPoint) {
    _history.add(HistoryPageInfo(itemList: itemList, status: pageStatus));

    print(this.isSelected.toString());
    Status nextStatus = Status.Over;
    List<Point>? nextItemList;
    switch (pageStatus) {
      case Status.Province:
        _onProvinceSelect(targetPoint);
        nextStatus = Status.City;
        nextItemList = targetProvince.child;
        if (!widget.showType.contain(ShowType.c)) {
          nextStatus = Status.Over;
        }
        if (nextItemList.isEmpty) {
          targetCity = null;
          targetArea = null;
          nextStatus = Status.Over;
        }
        break;
      case Status.City:
        _onCitySelect(targetPoint);
        nextStatus = Status.Area;
        nextItemList = targetCity?.child;
        if (!widget.showType.contain(ShowType.a)) {
          nextStatus = Status.Over;
        }
        if (nextItemList == null || nextItemList.isEmpty) {
          targetArea = null;
          nextStatus = Status.Over;
        }
        break;
      case Status.Area:
        nextStatus = Status.Over;
        _onAreaSelect(targetPoint);
        break;
      case Status.Over:
        break;
    }

    setTimeout(
        milliseconds: 300,
        callback: () {
          if (nextItemList == null || nextStatus == Status.Over) {
            return popHome();
          }
          if (mounted) {
            this.setState(() {
              itemList = nextItemList!;
              pageStatus = nextStatus;
            });
            scrollController.jumpTo(0.0);
          }
        });
  }

  Widget _buildHead() {
    String title = '请选择城市';
    switch (pageStatus) {
      case Status.Province:
        this.isSelected = false;

        break;
      case Status.City:
        title = targetProvince.name;
        this.isSelected = true;
        break;
      case Status.Area:
        title = targetCity!.name;
        this.isSelected = true;
        break;
      case Status.Over:
        break;
    }
    return Text(title);
  }

  List cityList = ["北京", "杭州", "宁波", "温州", "上海", "深圳"];
  bool isSelected = false;

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: back,
      child: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          title: _buildHead(),
        ),
        body: SafeArea(
            bottom: true,
            child:
            // ListWidget(
            //   itemList: itemList,
            //   controller: scrollController,
            //   onSelect: _onItemSelect,
            //   selectedId: _getSelectedId(),
            // ),
            ListView(
              children: [
                Offstage(
                    offstage: isSelected,
                    child: Column(
                      children: [
                        Container(
                          height: 50, child:Text('${_locationResult==null?"重新定位":_locationResult['city']}')
                        // LocationNamePage()
                        // Text("重新定位"),
                        ),
                        Container(
                          height: 100,
                          // decoration: new BoxDecoration(
                          //     border: new Border.all(color: Colors.grey,
                          //         width: 0.5),),
                          child: GridView.builder(
                            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                                crossAxisCount: 3,
                                crossAxisSpacing: 10,
                                childAspectRatio:3,
                                mainAxisSpacing:10
                            ),
                            physics: NeverScrollableScrollPhysics(),
                            shrinkWrap: true,
                            itemBuilder: (context, index) {
                              return Container(
                                alignment: Alignment.center,
                                decoration: new BoxDecoration(
                                  border: new Border.all(
                                      color: Colors.grey, width: 0.5),),
                                height: 8,
                                child: Text("${cityList[index]}"),
                              );
                            },
                            itemCount: 6,
                          ),
                        ),
                      ],
                    )
                ),

                Container(
                  child:
                  ListWidget(
                    itemList: itemList,
                    controller: scrollController,
                    onSelect: _onItemSelect,
                    selectedId: _getSelectedId(),
                  ),
                )


              ],
            )


        ),
      ),
    );
  }


  ///定位
    Map<String, Object> _locationResult= new Map();

    AMapFlutterLocation _locationPlugin = new AMapFlutterLocation();
    StreamSubscription<Map<String, Object>> _locationListener = new StreamSubscription();

    ///获取iOS native的accuracyAuthorization类型
    void requestAccuracyAuthorization() async {
      AMapAccuracyAuthorization currentAccuracyAuthorization =
      await _locationPlugin.getSystemAccuracyAuthorization();
      if (currentAccuracyAuthorization ==
          AMapAccuracyAuthorization.AMapAccuracyAuthorizationFullAccuracy) {
        print("精确定位类型");
      } else if (currentAccuracyAuthorization ==
          AMapAccuracyAuthorization.AMapAccuracyAuthorizationReducedAccuracy) {
        print("模糊定位类型");
      } else {
        print("未知定位类型");
      }
    }

    /// 动态申请定位权限
    void requestPermission() async {
      // 申请权限
      bool hasLocationPermission = await requestLocationPermission();
      if (hasLocationPermission) {
        print("定位权限申请通过");
      } else {
        print("定位权限申请不通过");
      }
    }

    /// 申请定位权限
    /// 授予定位权限返回true, 否则返回false
    Future<bool> requestLocationPermission() async {
      //获取当前的权限
      var status = await Permission.location.status;
      if (status == PermissionStatus.granted) {
        //已经授权
        return true;
      } else {
        //未授权则发起一次申请
        status = await Permission.location.request();
        if (status == PermissionStatus.granted) {
          return true;
        } else {
          return false;
        }
      }
    }

    ///设置定位参数
    void _setLocationOption() {
      AMapLocationOption locationOption = new AMapLocationOption();

      ///是否单次定位
      locationOption.onceLocation = false;

      ///是否需要返回逆地理信息
      locationOption.needAddress = true;

      ///逆地理信息的语言类型
      locationOption.geoLanguage = GeoLanguage.DEFAULT;

      locationOption.desiredLocationAccuracyAuthorizationMode =
          AMapLocationAccuracyAuthorizationMode.ReduceAccuracy;

      locationOption.fullAccuracyPurposeKey = "AMapLocationScene";

      ///设置Android端连续定位的定位间隔
      locationOption.locationInterval = 2000;

      ///设置Android端的定位模式<br>
      ///可选值:<br>
      ///<li>[AMapLocationMode.Battery_Saving]</li>
      ///<li>[AMapLocationMode.Device_Sensors]</li>
      ///<li>[AMapLocationMode.Hight_Accuracy]</li>
      locationOption.locationMode = AMapLocationMode.Hight_Accuracy;

      ///设置iOS端的定位最小更新距离<br>
      locationOption.distanceFilter = -1;

      ///设置iOS端期望的定位精度
      /// 可选值:<br>
      /// <li>[DesiredAccuracy.Best] 最高精度</li>
      /// <li>[DesiredAccuracy.BestForNavigation] 适用于导航场景的高精度 </li>
      /// <li>[DesiredAccuracy.NearestTenMeters] 10米 </li>
      /// <li>[DesiredAccuracy.Kilometer] 1000米</li>
      /// <li>[DesiredAccuracy.ThreeKilometers] 3000米</li>
      locationOption.desiredAccuracy = DesiredAccuracy.Best;

      ///设置iOS端是否允许系统暂停定位
      locationOption.pausesLocationUpdatesAutomatically = false;

      ///将定位参数设置给定位插件
      _locationPlugin.setLocationOption(locationOption);
    }

    ///开始定位
    void _startLocation() {
      ///开始定位之前设置定位参数
      _setLocationOption();
      _locationPlugin.startLocation();
      // const timeout = const Duration(seconds: 5);
      // Timer(timeout, () {
      //callback function
      _stopLocation(); // 5s之后
      // });

    }

    ///停止定位
    void _stopLocation() {
      _locationPlugin.stopLocation();
    }
///
///   // @override
  void dispose() {
    super.dispose();

    ///移除定位监听
    if (null != _locationListener) {
      _locationListener.cancel();
    }

    ///销毁定位
    _locationPlugin.destroy();
  }
}

class ListWidget extends StatelessWidget {
  final List<Point> itemList;
  final ScrollController controller;
  final int selectedId;
  final ValueChanged<Point> onSelect;

  ListWidget({required this.itemList,
    required this.onSelect,
    required this.controller,
    required this.selectedId});

  @override
  Widget build(BuildContext context) {
    ThemeData theme = Theme.of(context);
    return ListView.builder(
      controller: controller,
      physics: NeverScrollableScrollPhysics(),
      shrinkWrap: true,
      itemBuilder: (BuildContext context, int index) {
        Point item = itemList[index];
        return Container(
          decoration: BoxDecoration(
              border: Border(
                  bottom: BorderSide(color: theme.dividerColor, width: 1.0))),
          child: ListTileTheme(
            child: ListTile(
              title: Text(item.name),
              // item 标题
              dense: true,
              // item 直观感受是整体大小
              trailing: selectedId == item.code
                  ? Icon(Icons.check, color: theme.primaryColor)
                  : null,
              contentPadding: EdgeInsets.fromLTRB(24.0, .0, 24.0, 3.0),
              // item 内容内边距
              enabled: true,
              onTap: () {
                onSelect(itemList[index]);
              },
              // item onTap 点击事件
              onLongPress: () {},
              // item onLongPress 长按事件
              selected: selectedId == item.code, // item 是否选中状态
            ),
          ),
        );
      },
      itemCount: itemList.length,
    );
  }
}




©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容