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();
}

// 界面狀態(tài)
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 初始化地理位置信息失敗, 請檢查省分城市數(shù)據(jù) \n $e');
    }


    requestPermission();

    ///設(shè)置Android和iOS的apiKey<br>
    ///key的申請請參考高德開放平臺官網(wǎng)說明<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();
    // }

    ///注冊定位結(jié)果監(jiān)聽
    _locationListener = _locationPlugin.onLocationChanged().listen((Map<String, Object> result) {
      setState(() {
        _locationResult = result;
      });

      //             _locationResult['province'] +
      //             _locationResult['city'] +
      //             _locationResult['district'] +
      //             _locationResult['street'];
      print("定位結(jié)果${_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');
      // 此處兼容, 部分城市下無地區(qū)信息的情況
    }

    // 臺灣異常數(shù)據(jù). 需要過濾
    // 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("未知定位類型");
      }
    }

    /// 動態(tài)申請定位權(quán)限
    void requestPermission() async {
      // 申請權(quán)限
      bool hasLocationPermission = await requestLocationPermission();
      if (hasLocationPermission) {
        print("定位權(quán)限申請通過");
      } else {
        print("定位權(quán)限申請不通過");
      }
    }

    /// 申請定位權(quán)限
    /// 授予定位權(quán)限返回true阔籽, 否則返回false
    Future<bool> requestLocationPermission() async {
      //獲取當前的權(quán)限
      var status = await Permission.location.status;
      if (status == PermissionStatus.granted) {
        //已經(jīng)授權(quán)
        return true;
      } else {
        //未授權(quán)則發(fā)起一次申請
        status = await Permission.location.request();
        if (status == PermissionStatus.granted) {
          return true;
        } else {
          return false;
        }
      }
    }

    ///設(shè)置定位參數(shù)
    void _setLocationOption() {
      AMapLocationOption locationOption = new AMapLocationOption();

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

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

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

      locationOption.desiredLocationAccuracyAuthorizationMode =
          AMapLocationAccuracyAuthorizationMode.ReduceAccuracy;

      locationOption.fullAccuracyPurposeKey = "AMapLocationScene";

      ///設(shè)置Android端連續(xù)定位的定位間隔
      locationOption.locationInterval = 2000;

      ///設(shè)置Android端的定位模式<br>
      ///可選值:<br>
      ///<li>[AMapLocationMode.Battery_Saving]</li>
      ///<li>[AMapLocationMode.Device_Sensors]</li>
      ///<li>[AMapLocationMode.Hight_Accuracy]</li>
      locationOption.locationMode = AMapLocationMode.Hight_Accuracy;

      ///設(shè)置iOS端的定位最小更新距離<br>
      locationOption.distanceFilter = -1;

      ///設(shè)置iOS端期望的定位精度
      /// 可選值:<br>
      /// <li>[DesiredAccuracy.Best] 最高精度</li>
      /// <li>[DesiredAccuracy.BestForNavigation] 適用于導(dǎo)航場景的高精度 </li>
      /// <li>[DesiredAccuracy.NearestTenMeters] 10米 </li>
      /// <li>[DesiredAccuracy.Kilometer] 1000米</li>
      /// <li>[DesiredAccuracy.ThreeKilometers] 3000米</li>
      locationOption.desiredAccuracy = DesiredAccuracy.Best;

      ///設(shè)置iOS端是否允許系統(tǒng)暫停定位
      locationOption.pausesLocationUpdatesAutomatically = false;

      ///將定位參數(shù)設(shè)置給定位插件
      _locationPlugin.setLocationOption(locationOption);
    }

    ///開始定位
    void _startLocation() {
      ///開始定位之前設(shè)置定位參數(shù)
      _setLocationOption();
      _locationPlugin.startLocation();
      // const timeout = const Duration(seconds: 5);
      // Timer(timeout, () {
      //callback function
      _stopLocation(); // 5s之后
      // });

    }

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

    ///移除定位監(jiān)聽
    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 內(nèi)容內(nèi)邊距
              enabled: true,
              onTap: () {
                onSelect(itemList[index]);
              },
              // item onTap 點擊事件
              onLongPress: () {},
              // item onLongPress 長按事件
              selected: selectedId == item.code, // item 是否選中狀態(tài)
            ),
          ),
        );
      },
      itemCount: itemList.length,
    );
  }
}




?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闪盔,一起剝皮案震驚了整個濱河市融求,隨后出現(xiàn)的幾起案子刷允,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異姑裂,居然都是意外死亡,警方通過查閱死者的電腦和手機男旗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門舶斧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人察皇,你說我怎么就攤上這事茴厉。” “怎么了什荣?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵矾缓,是天一觀的道長。 經(jīng)常有香客問我稻爬,道長嗜闻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任桅锄,我火速辦了婚禮琉雳,結(jié)果婚禮上样眠,老公的妹妹穿的比我還像新娘。我一直安慰自己翠肘,他們只是感情好檐束,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著束倍,像睡著了一般被丧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肌幽,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天晚碾,我揣著相機與錄音,去河邊找鬼喂急。 笑死,一個胖子當著我的面吹牛笛求,可吹牛的內(nèi)容都是我干的廊移。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼探入,長吁一口氣:“原來是場噩夢啊……” “哼狡孔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蜂嗽,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤苗膝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后植旧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辱揭,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年病附,在試婚紗的時候發(fā)現(xiàn)自己被綠了问窃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡完沪,死狀恐怖域庇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情覆积,我是刑警寧澤听皿,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站宽档,受9級特大地震影響尉姨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜雌贱,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一啊送、第九天 我趴在偏房一處隱蔽的房頂上張望偿短。 院中可真熱鬧,春花似錦馋没、人聲如沸昔逗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽勾怒。三九已至,卻和暖如春声旺,著一層夾襖步出監(jiān)牢的瞬間笔链,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工腮猖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鉴扫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓澈缺,卻偏偏與公主長得像坪创,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子姐赡,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

推薦閱讀更多精彩內(nèi)容