├── address_picker ├── CHANGELOG.md ├── .metadata ├── LICENSE ├── README.md ├── .gitignore ├── pubspec.yaml └── lib │ ├── address_manager.dart │ ├── address_model.dart │ └── address_picker.dart ├── .gitignore ├── LICENSE └── README.md /address_picker/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.0.1] - TODO: Add release date. 2 | 3 | * TODO: Describe initial release. 4 | -------------------------------------------------------------------------------- /address_picker/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 2d2a1ffec95cc70a3218872a2cd3f8de4933c42f 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | .pub/ 7 | build/ 8 | # If you're building an application, you may want to check-in your pubspec.lock 9 | pubspec.lock 10 | 11 | # Directory created by dartdoc 12 | # If you don't generate documentation locally you can remove this line. 13 | doc/api/ 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dean Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /address_picker/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dean Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # address_picker 2 | Flutter城市选择器, 省市区选择器 3 | 4 | ## Usage 5 | 6 | ```dart 7 | class _HomePageState extends State { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | body: Center( 12 | child: FlatButton( 13 | child: Text('show'), 14 | onPressed: () { 15 | showModalBottomSheet( 16 | context: context, 17 | builder: (context) => BottomSheet( 18 | onClosing: () {}, 19 | builder: (context) => Container( 20 | height: 250.0, 21 | child: AddressPicker( 22 | style: TextStyle(color: Colors.black, fontSize: 17), 23 | mode: AddressPickerMode.provinceCityAndDistrict, 24 | onSelectedAddressChanged: (address) { 25 | print('${address.currentProvince.province}'); 26 | print('${address.currentCity.city}'); 27 | print('${address.currentDistrict.area}'); 28 | }, 29 | ), 30 | ))); 31 | }, 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | ``` 38 | 39 | ### Property 40 | 41 | - **mode**: 42 | 43 | ```dart 44 | /// 选择模式 45 | /// province 一级: 省 46 | /// provinceAndCity 二级: 省市 47 | /// provinceCityAndDistrict 三级: 省市区 48 | final AddressPickerMode mode; 49 | ``` 50 | 51 | - **onSelectedAddressChanged**: 52 | 53 | ```dart 54 | /// 选中的地址发生改变回调 55 | final AddressCallback onSelectedAddressChanged; 56 | ``` 57 | 58 | - **style**: 59 | 60 | ```dart 61 | /// 省市区文字显示样式 62 | final TextStyle style; 63 | ``` 64 | 65 | -------------------------------------------------------------------------------- /address_picker/README.md: -------------------------------------------------------------------------------- 1 | # address_picker 2 | Flutter城市选择器, 省市区选择器 3 | 4 | ## Usage 5 | 6 | ```dart 7 | class _HomePageState extends State { 8 | @override 9 | Widget build(BuildContext context) { 10 | return Scaffold( 11 | body: Center( 12 | child: FlatButton( 13 | child: Text('show'), 14 | onPressed: () { 15 | showModalBottomSheet( 16 | context: context, 17 | builder: (context) => BottomSheet( 18 | onClosing: () {}, 19 | builder: (context) => Container( 20 | height: 250.0, 21 | child: AddressPicker( 22 | style: TextStyle(color: Colors.black, fontSize: 17), 23 | mode: AddressPickerMode.provinceCityAndDistrict, 24 | onSelectedAddressChanged: (address) { 25 | print('${address.currentProvince.province}'); 26 | print('${address.currentCity.city}'); 27 | print('${address.currentDistrict.area}'); 28 | }, 29 | ), 30 | ))); 31 | }, 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | ``` 38 | 39 | ### Property 40 | 41 | - **mode**: 42 | 43 | ```dart 44 | /// 选择模式 45 | /// province 一级: 省 46 | /// provinceAndCity 二级: 省市 47 | /// provinceCityAndDistrict 三级: 省市区 48 | final AddressPickerMode mode; 49 | ``` 50 | 51 | - **onSelectedAddressChanged**: 52 | 53 | ```dart 54 | /// 选中的地址发生改变回调 55 | final AddressCallback onSelectedAddressChanged; 56 | ``` 57 | 58 | - **style**: 59 | 60 | ```dart 61 | /// 省市区文字显示样式 62 | final TextStyle style; 63 | ``` -------------------------------------------------------------------------------- /address_picker/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/Flutter/flutter_export_environment.sh 65 | **/ios/ServiceDefinitions.json 66 | **/ios/Runner/GeneratedPluginRegistrant.* 67 | 68 | # Exceptions to above rules. 69 | !**/ios/**/default.mode1v3 70 | !**/ios/**/default.mode2v3 71 | !**/ios/**/default.pbxuser 72 | !**/ios/**/default.perspectivev3 73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 74 | -------------------------------------------------------------------------------- /address_picker/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: address_picker 2 | description: Flutter城市选择器, 省市区选择器 3 | version: 0.0.1 4 | author: chendaxin 5 | homepage: https://github.com/SiriDx/address_picker 6 | 7 | environment: 8 | sdk: ">=2.1.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://dart.dev/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | 24 | assets: 25 | - assets/address.json 26 | 27 | # To add assets to your package, add an assets section, like this: 28 | # assets: 29 | # - images/a_dot_burr.jpeg 30 | # - images/a_dot_ham.jpeg 31 | # 32 | # For details regarding assets in packages, see 33 | # https://flutter.dev/assets-and-images/#from-packages 34 | # 35 | # An image asset can refer to one or more resolution-specific "variants", see 36 | # https://flutter.dev/assets-and-images/#resolution-aware. 37 | 38 | # To add custom fonts to your package, add a fonts section here, 39 | # in this "flutter" section. Each entry in this list should have a 40 | # "family" key with the font family name, and a "fonts" key with a 41 | # list giving the asset and other descriptors for the font. For 42 | # example: 43 | # fonts: 44 | # - family: Schyler 45 | # fonts: 46 | # - asset: fonts/Schyler-Regular.ttf 47 | # - asset: fonts/Schyler-Italic.ttf 48 | # style: italic 49 | # - family: Trajan Pro 50 | # fonts: 51 | # - asset: fonts/TrajanPro.ttf 52 | # - asset: fonts/TrajanPro_Bold.ttf 53 | # weight: 700 54 | # 55 | # For details regarding fonts in packages, see 56 | # https://flutter.dev/custom-fonts/#from-packages 57 | -------------------------------------------------------------------------------- /address_picker/lib/address_manager.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'address_model.dart'; 5 | 6 | abstract class AddressManager { 7 | 8 | static List _provinces; 9 | static Map _provinceMap = Map(); 10 | static Map _cityMap = Map(); 11 | static Map _districtMap = Map(); 12 | 13 | static Future> loadAddressData ( 14 | BuildContext context) async { 15 | 16 | if (_provinces != null && _provinces.isNotEmpty) { 17 | return _provinces; 18 | } 19 | var address = await rootBundle.loadString('packages/address_picker/assets/address.json'); 20 | var data = json.decode(address); 21 | var provinces = new List(); 22 | if (json != null && data is List) { 23 | data.forEach((v) { 24 | var province = AddressProvince.fromJson(v, cityMap: _cityMap, districtMap: _districtMap); 25 | _provinceMap[province.provinceid] = province; 26 | provinces.add(province); 27 | }); 28 | _provinces = provinces; 29 | return _provinces; 30 | } 31 | return List(); 32 | } 33 | 34 | static Future getProvince(BuildContext context, String provinceId) async { 35 | if (_provinceMap.isEmpty) { 36 | var provinces = await loadAddressData(context); 37 | if (provinces.isNotEmpty) { 38 | return _provinceMap[provinceId]; 39 | } 40 | return null; 41 | } else { 42 | return _provinceMap[provinceId]; 43 | } 44 | } 45 | 46 | static Future getCity(BuildContext context, String cityId) async { 47 | if (_cityMap.isEmpty) { 48 | var provinces = await loadAddressData(context); 49 | if (provinces.isNotEmpty) { 50 | return _cityMap[cityId]; 51 | } 52 | return null; 53 | } else { 54 | return _cityMap[cityId]; 55 | } 56 | } 57 | 58 | static Future getDistrict(BuildContext context, String districtId) async { 59 | if (_districtMap.isEmpty) { 60 | var provinces = await loadAddressData(context); 61 | if (provinces.isNotEmpty) { 62 | return _districtMap[districtId]; 63 | } 64 | return null; 65 | } else { 66 | return _districtMap[districtId]; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /address_picker/lib/address_model.dart: -------------------------------------------------------------------------------- 1 | class AddressProvince { 2 | int id; 3 | String provinceid; 4 | String province; 5 | String lng; 6 | String lat; 7 | List cities; 8 | 9 | AddressProvince( 10 | {this.id, 11 | this.provinceid, 12 | this.province, 13 | this.lng, 14 | this.lat, 15 | this.cities}); 16 | 17 | AddressProvince.fromJson(Map json, {Map cityMap, Map districtMap}) { 18 | id = json['id']; 19 | provinceid = json['provinceid']; 20 | province = json['province']; 21 | lng = json['lng']; 22 | lat = json['lat']; 23 | if (json['cities'] != null) { 24 | cities = new List(); 25 | json['cities'].forEach((v) { 26 | var city = AddressCity.fromJson(v, districtMap: districtMap); 27 | cityMap[city.cityid] = city; 28 | cities.add(city); 29 | }); 30 | } 31 | } 32 | 33 | Map toJson() { 34 | final Map data = new Map(); 35 | data['id'] = this.id; 36 | data['provinceid'] = this.provinceid; 37 | data['province'] = this.province; 38 | data['lng'] = this.lng; 39 | data['lat'] = this.lat; 40 | if (this.cities != null) { 41 | data['cities'] = this.cities.map((v) => v.toJson()).toList(); 42 | } 43 | return data; 44 | } 45 | } 46 | 47 | class AddressCity { 48 | int id; 49 | String city; 50 | String cityid; 51 | String provinceid; 52 | String lng; 53 | String lat; 54 | List district; 55 | 56 | AddressCity( 57 | {this.id, 58 | this.city, 59 | this.cityid, 60 | this.provinceid, 61 | this.lng, 62 | this.lat, 63 | this.district}); 64 | 65 | AddressCity.fromJson(Map json, {Map districtMap}) { 66 | id = json['id']; 67 | city = json['city']; 68 | cityid = json['cityid']; 69 | provinceid = json['provinceid']; 70 | lng = json['lng']; 71 | lat = json['lat']; 72 | if (json['district'] != null) { 73 | district = new List(); 74 | json['district'].forEach((v) { 75 | var dis = AddressDistrict.fromJson(v); 76 | districtMap[dis.areaid] = dis; 77 | district.add(dis); 78 | }); 79 | } 80 | } 81 | 82 | Map toJson() { 83 | final Map data = new Map(); 84 | data['id'] = this.id; 85 | data['city'] = this.city; 86 | data['cityid'] = this.cityid; 87 | data['provinceid'] = this.provinceid; 88 | data['lng'] = this.lng; 89 | data['lat'] = this.lat; 90 | if (this.district != null) { 91 | data['district'] = this.district.map((v) => v.toJson()).toList(); 92 | } 93 | return data; 94 | } 95 | } 96 | 97 | class AddressDistrict { 98 | int id; 99 | String area; 100 | String areaid; 101 | String cityid; 102 | String lng; 103 | String lat; 104 | 105 | AddressDistrict({this.id, this.area, this.areaid, this.cityid, this.lng, this.lat}); 106 | 107 | AddressDistrict.fromJson(Map json) { 108 | id = json['id']; 109 | area = json['area']; 110 | areaid = json['areaid']; 111 | cityid = json['cityid']; 112 | lng = json['lng']; 113 | lat = json['lat']; 114 | } 115 | 116 | Map toJson() { 117 | final Map data = new Map(); 118 | data['id'] = this.id; 119 | data['area'] = this.area; 120 | data['areaid'] = this.areaid; 121 | data['cityid'] = this.cityid; 122 | data['lng'] = this.lng; 123 | data['lat'] = this.lat; 124 | return data; 125 | } 126 | } -------------------------------------------------------------------------------- /address_picker/lib/address_picker.dart: -------------------------------------------------------------------------------- 1 | library address_picker; 2 | 3 | import 'package:address_picker/address_manager.dart'; 4 | import 'package:flutter/cupertino.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'address_model.dart'; 7 | 8 | class Address { 9 | AddressProvince currentProvince; 10 | AddressCity currentCity; 11 | AddressDistrict currentDistrict; 12 | 13 | Address({this.currentProvince, this.currentCity, this.currentDistrict}); 14 | } 15 | 16 | typedef AddressCallback = void Function(Address); 17 | 18 | enum AddressPickerMode { 19 | province, 20 | provinceAndCity, 21 | provinceCityAndDistrict, 22 | } 23 | 24 | class AddressPicker extends StatefulWidget { 25 | 26 | /// 选中的地址发生改变回调 27 | final AddressCallback onSelectedAddressChanged; 28 | 29 | /// 选择模式 30 | /// province 一级: 省 31 | /// provinceAndCity 二级: 省市 32 | /// provinceCityAndDistrict 三级: 省市区 33 | final AddressPickerMode mode; 34 | 35 | /// 省市区文字显示样式 36 | final TextStyle style; 37 | 38 | AddressPicker( 39 | {Key key, 40 | this.mode = AddressPickerMode.provinceCityAndDistrict, 41 | this.onSelectedAddressChanged, 42 | this.style = const TextStyle(color: Colors.black, fontSize: 17)}) 43 | : super(key: key); 44 | 45 | _AddressPickerState createState() => _AddressPickerState(); 46 | } 47 | 48 | class _AddressPickerState extends State { 49 | List _provinces; 50 | 51 | AddressProvince _selectedProvince; 52 | AddressCity _selectedCity; 53 | AddressDistrict _selectedDistrict; 54 | 55 | FixedExtentScrollController _cityScrollController = 56 | FixedExtentScrollController(initialItem: 0); 57 | FixedExtentScrollController _districtScrollController = 58 | FixedExtentScrollController(initialItem: 0); 59 | 60 | @override 61 | void dispose() { 62 | _cityScrollController.dispose(); 63 | _districtScrollController.dispose(); 64 | super.dispose(); 65 | } 66 | 67 | @override 68 | void initState() { 69 | super.initState(); 70 | _getAddressData(); 71 | } 72 | 73 | void _getAddressData() async { 74 | var addressData = await AddressManager.loadAddressData(context); 75 | setState(() { 76 | _provinces = addressData; 77 | _selectedProvince = _provinces.first; 78 | _selectedCity = _selectedProvince.cities.first; 79 | _selectedDistrict = _selectedCity.district.first; 80 | }); 81 | } 82 | 83 | void _updateCurrent() { 84 | if (widget.onSelectedAddressChanged != null) { 85 | var address = Address( 86 | currentProvince: _selectedProvince, 87 | currentCity: _selectedCity, 88 | currentDistrict: _selectedDistrict); 89 | widget.onSelectedAddressChanged(address); 90 | } 91 | } 92 | 93 | @override 94 | Widget build(BuildContext context) { 95 | if (_provinces == null || _provinces.isEmpty) { 96 | return Container(); 97 | } 98 | 99 | return Container( 100 | color: Colors.white, 101 | child: Row( 102 | children: [ 103 | Expanded( 104 | flex: 1, 105 | child: CupertinoPicker.builder( 106 | backgroundColor: Colors.white, 107 | childCount: _provinces?.length ?? 0, 108 | itemBuilder: (context, index) { 109 | var item = _provinces[index]; 110 | return Container( 111 | alignment: Alignment.center, 112 | child: Text( 113 | item.province, 114 | style: widget.style, 115 | ), 116 | ); 117 | }, 118 | itemExtent: 44, 119 | onSelectedItemChanged: (item) { 120 | setState(() { 121 | _selectedProvince = _provinces[item]; 122 | _selectedCity = _selectedProvince.cities.first; 123 | _selectedDistrict = _selectedCity.district.first; 124 | _cityScrollController.animateToItem(0, 125 | curve: Curves.easeInOut, 126 | duration: Duration(milliseconds: 250)); 127 | _districtScrollController.animateToItem(0, 128 | curve: Curves.easeInOut, 129 | duration: Duration(milliseconds: 250)); 130 | }); 131 | _updateCurrent(); 132 | }, 133 | ), 134 | ), 135 | widget.mode == AddressPickerMode.province 136 | ? Container() 137 | : Expanded( 138 | flex: 1, 139 | child: CupertinoPicker.builder( 140 | scrollController: _cityScrollController, 141 | backgroundColor: Colors.white, 142 | childCount: _selectedProvince?.cities?.length ?? 0, 143 | itemBuilder: (context, index) { 144 | var item = _selectedProvince.cities[index]; 145 | return Container( 146 | alignment: Alignment.center, 147 | child: Text( 148 | item.city, 149 | style: widget.style, 150 | ), 151 | ); 152 | }, 153 | itemExtent: 44, 154 | onSelectedItemChanged: (item) { 155 | setState(() { 156 | _selectedCity = _selectedProvince.cities[item]; 157 | _selectedDistrict = _selectedCity.district.first; 158 | _districtScrollController.animateToItem(0, 159 | curve: Curves.easeInOut, 160 | duration: Duration(milliseconds: 250)); 161 | }); 162 | _updateCurrent(); 163 | }, 164 | )), 165 | widget.mode != AddressPickerMode.provinceCityAndDistrict 166 | ? Container() 167 | : Expanded( 168 | flex: 1, 169 | child: CupertinoPicker.builder( 170 | scrollController: _districtScrollController, 171 | backgroundColor: Colors.white, 172 | childCount: _selectedCity?.district?.length ?? 0, 173 | itemBuilder: (context, index) { 174 | var item = _selectedCity.district[index]; 175 | return Container( 176 | alignment: Alignment.center, 177 | child: Text( 178 | item.area, 179 | style: widget.style, 180 | ), 181 | ); 182 | }, 183 | itemExtent: 44, 184 | onSelectedItemChanged: (item) { 185 | var district = _selectedCity.district[item]; 186 | _selectedDistrict = district; 187 | _updateCurrent(); 188 | }, 189 | ), 190 | ), 191 | ], 192 | ), 193 | ); 194 | } 195 | } 196 | --------------------------------------------------------------------------------