尝试在 Flutter 中获取地点的自动完成位置并在点击的地点上显示详细信息时出现空安全错误
Posted
技术标签:
【中文标题】尝试在 Flutter 中获取地点的自动完成位置并在点击的地点上显示详细信息时出现空安全错误【英文标题】:Getting null safety errors when tried to get autocomplete location of places in Flutter and display details on tapped place 【发布时间】:2021-10-07 04:24:46 【问题描述】:我尝试将 no null 安全代码迁移到 null 安全,但最终出现错误。我想在 Flutter 中自动完成地点的位置,并在点击的地方显示详细信息。
错误截图:
代码:
main.dart
import 'package:flutter/material.dart';
import 'package:google_places_flutter/address_search.dart';
import 'package:google_places_flutter/place_service.dart';
import 'package:uuid/uuid.dart';
void main()
runApp(MyApp());
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Google Places Demo',
home: MyHomePage(title: 'Places Autocomplete Demo'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key? key, this.title) : super(key: key);
final String? title;
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
final _controller = TextEditingController();
String? _streetNumber = '';
String? _street = '';
String? _city = '';
String? _zipCode = '';
@override
void dispose()
_controller.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text(widget.title!),
),
body: Container(
margin: EdgeInsets.only(left: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextField(
controller: _controller,
readOnly: true,
onTap: () async
// generate a new token here
final sessionToken = Uuid().v4();
final Suggestion? result = await showSearch(
context: context,
delegate:AddressSearch(sessionToken),
);
// This will change the text displayed in the TextField
if (result != null)
final placeDetails = await PlaceApiProvider(sessionToken)
.getPlaceDetailFromId(result.placeId);
setState(()
_controller.text = result.description!;
_streetNumber = placeDetails.streetNumber;
_street = placeDetails.street;
_city = placeDetails.city;
_zipCode = placeDetails.zipCode;
);
,
decoration: InputDecoration(
icon: Container(
width: 10,
height: 10,
child: Icon(
Icons.home,
color: Colors.black,
),
),
hintText: "Enter address",
border: InputBorder.none,
contentPadding: EdgeInsets.only(left: 8.0, top: 16.0),
),
),
SizedBox(height: 20.0),
Text('Street Number: $_streetNumber'),
Text('Street: $_street'),
Text('City: $_city'),
Text('ZIP Code: $_zipCode'),
],
),
),
);
address_search.dart
import 'package:flutter/material.dart';
import 'package:google_places_flutter/place_service.dart';
class AddressSearch extends SearchDelegate<Suggestion?>
AddressSearch(this.sessionToken)
apiClient = PlaceApiProvider(sessionToken);
final sessionToken;
late PlaceApiProvider apiClient;
@override
List<Widget> buildActions(BuildContext context)
return [
IconButton(
tooltip: 'Clear',
icon: Icon(Icons.clear),
onPressed: ()
query = '';
,
)
];
@override
Widget buildLeading(BuildContext context)
return IconButton(
tooltip: 'Back',
icon: Icon(Icons.arrow_back),
onPressed: ()
close(context, null);
,
);
@override
Widget buildResults(BuildContext context)
return Container();
@override
Widget buildSuggestions(BuildContext context)
return FutureBuilder(
future: query == ""
? null
: apiClient.fetchSuggestions(
query, Localizations.localeOf(context).languageCode),
builder: (context, snapshot) => query == ''
? Container(
padding: EdgeInsets.all(16.0),
child: Text('Enter address'),
)
: snapshot.hasData
? ListView.builder(
itemBuilder: (context, index) =>
ListTile(
title:
Text((snapshot.data[index] as Suggestion).description!),
onTap: ()
close(context, snapshot.data[index] as Suggestion?);
,
),
itemCount: snapshot.data.length,
)
: Container(child: Text('Loading...')),
);
place_service.dart
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart';
class Place
String? streetNumber;
String? street;
String? city;
String? zipCode;
Place(
this.streetNumber,
this.street,
this.city,
this.zipCode,
);
@override
String toString()
return 'Place(streetNumber: $streetNumber, street: $street, city: $city, zipCode: $zipCode)';
class Suggestion
final String? placeId;
final String? description;
Suggestion(this.placeId, this.description);
@override
String toString()
return 'Suggestion(description: $description, placeId: $placeId)';
class PlaceApiProvider
final client = Client();
PlaceApiProvider(this.sessionToken);
final sessionToken;
static final String androidKey = 'YOUR_API_KEY_HERE';
static final String iosKey = 'YOUR_API_KEY_HERE';
final apiKey = Platform.isAndroid ? androidKey : iosKey;
Future<List<Suggestion>?> fetchSuggestions(String input, String lang) async
final request =
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input&key=$apiKey&sessiontoken=$sessionToken';
final response = await client.get(Uri.parse(request));
if (response.statusCode == 200)
final result = json.decode(response.body);
if (result['status'] == 'OK')
// compose suggestions in a list
return result['predictions']
.map<Suggestion>((p) => Suggestion(p['place_id'], p['description']))
.toList();
if (result['status'] == 'ZERO_RESULTS')
return [];
throw Exception(result['error_message']);
else
throw Exception('Failed to fetch suggestion');
Future<Place> getPlaceDetailFromId(String? placeId) async
final request =
'https://maps.googleapis.com/maps/api/place/details/json?place_id=$placeId&fields=address_component&key=$apiKey&sessiontoken=$sessionToken';
final response = await client.get(Uri.parse(request));
if (response.statusCode == 200)
final result = json.decode(response.body);
if (result['status'] == 'OK')
final components =
result['result']['address_components'] as List<dynamic>;
// build result
final place = Place();
components.forEach((c)
final List type = c['types'];
if (type.contains('street_number'))
place.streetNumber = c['long_name'];
if (type.contains('route'))
place.street = c['long_name'];
if (type.contains('locality'))
place.city = c['long_name'];
if (type.contains('postal_code'))
place.zipCode = c['long_name'];
);
return place;
throw Exception(result['error_message']);
else
throw Exception('Failed to fetch suggestion');
【问题讨论】:
尝试添加 bang(!
) 运算符,例如 snapshot.data!....
@YeasinSheikh 我尝试使用 bang(!) 运算符,但又出现了另一个错误 1) 运算符 '[]' 没有为类型 'Object' 定义:58 2) 运算符 '[]'没有为“对象”类型定义:60 3)没有为“对象”类型定义吸气剂“长度”:63
【参考方案1】:
解决办法大概是这样的:
builder: (context, AsyncSnapshot<List<Suggestion>> snapshot)
而不是这个:
builder: (context, snapshot)
然后您可以执行以下操作:
List<Suggestion>? suggestions = snapshot.data;
if ( suggestions != null && suggestions.length > 0)
【讨论】:
以上是关于尝试在 Flutter 中获取地点的自动完成位置并在点击的地点上显示详细信息时出现空安全错误的主要内容,如果未能解决你的问题,请参考以下文章