On my project I need to use several showDialog one after the other.
For user creation, I use a SearchField widget to retrieve info from a table related to the user.
If the SearchField value does not exist I would like to propose the creation. Depending on the choice either the form is in error or I propose to register the user.
For this I use a showDialog in the validator of the SearchField and an if validator is correct.
My problem is that my second dialog box is displayed before validating the first one and even above that of the SearchField.
What is the correct way to do this?
Thank you,
class InformationsPage extends StatefulWidget {
const InformationsPage({
required Key key,
required this.user,
required this.type,
}) : super(key: key);
final User user;
final FenType type;
#override
InformationsPageState createState() => InformationsPageState();
}
class InformationsPageState extends State<InformationsPage>
with AutomaticKeepAliveClientMixin {
InformationsPageState({this.user});
final User? user;
late UserApi _api;
#override
bool get wantKeepAlive => true;
bool _familyIsCreated = false;
late User userSaved;
late FenType type;
//Info Form
var _pseudoController = TextEditingController();
var _familyController = TextEditingController();
#override
void initState() {
super.initState();
_api = UserApi();
_pseudoController = TextEditingController(text: widget.user.pseudo);
_familyController = TextEditingController(text: widget.user.familyName);
userSaved = User.fromUser();
type = widget.type;
}
#override
void dispose() {
_pseudoController.dispose();
_familyController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
super.build(context);
return Column(
children: <Widget>[
FutureBuilder(
future: _api.getFamilies(),
builder: (context, AsyncSnapshot<List<Family>> snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(
"Something wrong with message: ${snapshot.error.toString()}"));
} else if (snapshot.connectionState == ConnectionState.done) {
List<Family> _list = snapshot.data!;
return _buildDropdownSearchFamilies(_list);
} else {
return const Center(child: CircularProgressIndicator());
}
}),
TextFormField(
readOnly: type == FenType.read ? true : false,
inputFormatters: [LowerCaseTextFormatter()],
controller: _pseudoController,
onSaved: (value) => userSaved.pseudo = value,
decoration: const InputDecoration(
icon: Icon(Icons.person),
hintText: 'Pseudo',
labelText: 'Pseudo',
),
validator: (value) =>
value!.isEmpty ? 'Obligatory' : null),
],
);
}
int? _contains(List<Family> list, String? name) {
int? res = -1;
for (Family element in list) {
if (element.name == name) {
res = element.id;
break;
}
}
return res;
}
Widget _buildDropdownSearchFamilies(List<Family> _list) {
return SearchField(
controller: _familyController,
suggestions: _list
.map((e) =>
SearchFieldListItem(e.name!, child: Text(e.name!), item: e.id))
.toList(),
hint: 'Family',
validator: (x) {
if (x!.isEmpty) {
userSaved.familyId = null;
userSaved.familyName = null;
return null;
}
int? id = _contains(_list, x);
if (id == -1) {
userSaved.familyId == null;
showDiaglog(x);
if (userSaved.familyId != null) {
return null;
} else {
return 'Family not exist';
}
} else {
userSaved.familyId = id;
userSaved.familyName = x;
return null;
}
},
searchInputDecoration: const InputDecoration(
labelText: 'Family', icon: Icon(Icons.groups)),
itemHeight: 50,
onTap: (x) {
userSaved.familyId = x.item as int?;
userSaved.familyName = x.child.toString();
});
}
showDiaglog(String family) async {
String title = "Family";
String message =
"Family $family not exist. Create ?";
String textKoButton = "no";
String textOkButton = "yes";
MyDialog alert = MyDialog(
title: title,
message: message,
onPressedKo: koButtonPressed(),
onPressedOk: okButtonPressed(family),
textKoButton: textKoButton,
textOkButton: textOkButton);
await showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
void Function() koButtonPressed() => () {
_familyIsCreated = false;
Navigator.of(context).pop(false);
};
void Function() okButtonPressed(family) => () {
_save(family);
Navigator.of(context).pop();
};
void _save(family) async {
UserApi apiUser = UserApi();
Family oldF = Family.empty();
Family newF = Family.empty();
newF.name = family;
newF.createdAt = oldF.createdAt;
newF.deletedAt = newF.deletedAt;
Map<String, dynamic> data = oldF.toJson(newF);
int res = -1;
res = await apiUser.createFamily(data);
SnackBar snackBar;
if (res != -1) {
snackBar = MyWidget.okSnackBar('Family created');
userSaved.familyId = res;
userSaved.familyName = family;
} else {
snackBar = MyWidget.koSnackBar(
'Family not created');
userSaved.familyId = null;
userSaved.familyName = null;
}
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
}
My form :
class UserFormPage extends StatefulWidget {
static const String routeName = '/admin/user-form';
final User? user;
final FenType fenType;
const UserFormPage({Key? key, required this.user, required this.fenType})
: super(key: key);
#override
_UserFormPageState createState() => _UserFormPageState();
}
class _UserFormPageState extends State<UserFormPage>
with SingleTickerProviderStateMixin {
static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_appState');
static final GlobalKey<InformationsPageState> _infoKey =
GlobalKey<InformationsPageState>();
late TabController _controller;
late User _user;
late User _userSaved;
#override
void initState() {
super.initState();
_controller = TabController(vsync: this, length: 2);
_user = widget.user!;
_userSaved = widget.user!;
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () =>
Navigator.pushReplacementNamed(context, Routes.admUserList),
),
title: const Text('Member'),
actions: <Widget>[
Visibility(
visible: widget.fenType != FenType.read ? true : false,
child: IconButton(
icon: const Icon(Icons.save),
onPressed: () {
if (!_formKey.currentState!.validate()) {
return;
}
showDiaglog();
},
))
],
bottom: TabBar(
controller: _controller,
tabs: const [
Tab(text: 'Info'),
Tab(text: 'Others'),
],
),
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Flexible(
child: TabBarView(
controller: _controller,
children: <Widget>[
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InformationsPage(
user: _user,
key: _infoKey,
type: widget.fenType),
])),
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DetailsPage(
user: _user,
key: _detailsKey,
type: widget.fenType)
],
)),
],
))
],
))),
);
}
void _save() async {
final infoState = _infoKey.currentState;
_userSaved = infoState?.userSaved ?? _user;
_userSaved.pseudo = infoState?.userSaved.pseudo ?? _user.pseudo;
Map<String, dynamic> data = _user.userToJsonClean(_userSaved);
if (!_userSaved.userIsUpdated()) {
final outSnackBar = MyWidget.okSnackBar('Not update');
ScaffoldMessenger.of(context).showSnackBar(outSnackBar);
} else {
UserApi apiUser = UserApi();
bool res = false;
res = widget.fenType == FenType.update
? await apiUser.update(data)
: await apiUser.create(data);
SnackBar snackBar;
res
? snackBar = MyWidget.okSnackBar('Member saved')
: snackBar = MyWidget.koSnackBar(
'Member not saved');
ScaffoldMessenger.of(context).showSnackBar(snackBar);
_user = _userSaved;
if (widget.fenType == FenType.create) {
Navigator.of(context).popAndPushNamed(Routes.admUserList);
}
}
}
void showDiaglog() {
String pseudo = _userSaved.pseudo!;
String title = "Save";
String message = widget.fenType == FenType.create
? "Create member $pseudo ?"
: "Save meber $pseudo ?";
String textKoButton = "no";
String textOkButton = "yes";
MyDialog alert = MyDialog(
title: title,
message: message,
onPressedKo: koButtonPressed(),
onPressedOk: okButtonPressed(),
textKoButton: textKoButton,
textOkButton: textOkButton);
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}
void Function() koButtonPressed() => () {
Navigator.of(context).pop(false);
};
void Function() okButtonPressed() => () {
_formKey.currentState!.save();
_save();
Navigator.of(context).pop();
};
}
I resolve this problem to modified the widget SearchField to a DropdownSearch.
Related
I am facing a couple of issues, I'm trying to integrate my Google Calendar inside my app, but I get some issues "CalendarScope isn't defined for the type 'CalendarApi'.
As you can see in the screenshot below, there are some issues, but I don't know how fix them.
I am taking it from this code
https://www.syncfusion.com/kb/12647/how-to-load-the-google-calendar-events-to-the-flutter-calendar-sfcalendar-in-ios
I leave you here my code, could you please help me to figure out how to fix this?
class CalendarPage extends StatefulWidget {
#override
_CalendarPage createState() => _CalendarPage();
}
class _CalendarPage extends State<CalendarPage> {
final GoogleSignIn _googleSignIn = GoogleSignIn(
clientId:
'OAuth Client ID',
scopes: <String>[
googleAPI.CalendarApi.CalendarScope,
],
);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue[900],
centerTitle: true,
elevation: 0,
title: const Text("Calendario Scadenze",
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w600, fontFamily: "Raleway"),
),
leading: Builder(
builder: (BuildContext context) {
return IconButton(
icon: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onPressed: () {
Navigator.of(context).pop();
},
);
},
),
),
backgroundColor: Colors.white,
body: FutureBuilder(
future: getGoogleEventsData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Stack(
children: [
SfCalendar(
view: CalendarView.month,
initialDisplayDate: DateTime(2020,7,15,9,0,0),
dataSource: GoogleDataSource(events: snapshot.data),
monthViewSettings: const MonthViewSettings(
appointmentDisplayMode:
MonthAppointmentDisplayMode.appointment),
),
snapshot.data != null
? Container()
: const Center(
child: CircularProgressIndicator(),
)
],
);
},
),
);
}
#override
void dispose(){
if(_googleSignIn.currentUser != null) {
_googleSignIn.disconnect();
_googleSignIn.signOut();
}
super.dispose();
}
Future<List<googleAPI.Event>> getGoogleEventsData() async {
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
final GoogleAPIClient httpClient =
GoogleAPIClient(await googleUser!.authHeaders);
final googleAPI.CalendarApi calendarAPI = googleAPI.CalendarApi(httpClient);
final googleAPI.Events calEvents = await calendarAPI.events.list(
"primary",
);
final List<googleAPI.Event> appointments = <googleAPI.Event>[];
if (calEvents.items != null) {
for (int i = 0; i < calEvents.items!.length; i++) {
final googleAPI.Event event = calEvents.items![i];
if (event.start == null) {
continue;
}
appointments.add(event);
}
}
return appointments;
}
}
class GoogleDataSource extends CalendarDataSource {
GoogleDataSource({required List<googleAPI.Event> events}) {
appointments = events;
}
#override
DateTime getStartTime(int index) {
final googleAPI.Event event = appointments![index];
return event.start!.date ?? event.start!.dateTime!.toLocal();
}
#override
bool isAllDay(int index) {
return appointments![index].start.date != null;
}
#override
DateTime getEndTime(int index) {
final googleAPI.Event event = appointments![index];
return event.endTimeUnspecified != null
//&& event.endTimeUnspecified
? (event.start!.date ?? event.start!.dateTime!.toLocal())
: (event.end!.date != null
? event.end!.date!.add(const Duration(days: -1))
: event.end!.dateTime!.toLocal());
}
#override
String getLocation(int index) {
return appointments![index].location;
}
#override
String getNotes(int index) {
return appointments![index].description;
}
#override
String? getSubject(int index) {
final googleAPI.Event event = appointments![index];
return event.summary == null || event.summary!.isEmpty
? 'No Title'
: event.summary;
}
}
class GoogleAPIClient extends IOClient {
final Map<String, String> _headers;
GoogleAPIClient(this._headers) : super();
#override
Future<IOStreamedResponse> send(BaseRequest request) =>
super.send(request..headers.addAll(_headers));
#override
Future<Response> head(Object url, {required Map<String, String> headers}) =>
super.head(url, headers: headers..addAll(_headers));
}
I am working on a web app where users can post stuffs and make them more accessible by associating the posts with tags. so my idea is similar to stackoverflow's way of giving tags to posts, I am creating a Textfield with which will accept only few tags(string values) which I will create from a list and users can put them in their post. But I aint getting how to implement this as textfield has only few keyboardtypes... and I what I want to achieve is if I entered a value from a that list then it should act like a chip text(tag).
or Is there any other way to do this,
your help is appreciated,
thank you
Yes, there is. you can use the flutter_tagging package on the PUB
It has supports for Web
The gif below explains what you want to achieve
You can find an implementation of a Chip Input Field type widget here:
Latest: https://gist.github.com/slightfoot/c6c0f1f1baca326a389a9aec47886ad6
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// See: https://twitter.com/shakil807/status/1042127387515858949
// https://github.com/pchmn/MaterialChipsInput/tree/master/library/src/main/java/com/pchmn/materialchips
// https://github.com/BelooS/ChipsLayoutManager
void main() => runApp(ChipsDemoApp());
class ChipsDemoApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primaryColor: Colors.indigo,
accentColor: Colors.pink,
),
home: DemoScreen(),
);
}
}
class DemoScreen extends StatefulWidget {
#override
_DemoScreenState createState() => _DemoScreenState();
}
class _DemoScreenState extends State<DemoScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Material Chips Input'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
decoration: const InputDecoration(hintText: 'normal'),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ChipsInput<AppProfile>(
decoration: InputDecoration(prefixIcon: Icon(Icons.search), hintText: 'Profile search'),
findSuggestions: _findSuggestions,
onChanged: _onChanged,
chipBuilder: (BuildContext context, ChipsInputState<AppProfile> state, AppProfile profile) {
return InputChip(
key: ObjectKey(profile),
label: Text(profile.name),
avatar: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
onDeleted: () => state.deleteChip(profile),
onSelected: (_) => _onChipTapped(profile),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
);
},
suggestionBuilder: (BuildContext context, ChipsInputState<AppProfile> state, AppProfile profile) {
return ListTile(
key: ObjectKey(profile),
leading: CircleAvatar(
backgroundImage: NetworkImage(profile.imageUrl),
),
title: Text(profile.name),
subtitle: Text(profile.email),
onTap: () => state.selectSuggestion(profile),
);
},
),
),
),
],
),
);
}
void _onChipTapped(AppProfile profile) {
print('$profile');
}
void _onChanged(List<AppProfile> data) {
print('onChanged $data');
}
Future<List<AppProfile>> _findSuggestions(String query) async {
if (query.length != 0) {
return mockResults.where((profile) {
return profile.name.contains(query) || profile.email.contains(query);
}).toList(growable: false);
} else {
return const <AppProfile>[];
}
}
}
// -------------------------------------------------
const mockResults = <AppProfile>[
AppProfile('Stock Man', 'stock#man.com', 'https://d2gg9evh47fn9z.cloudfront.net/800px_COLOURBOX4057996.jpg'),
AppProfile('Paul', 'paul#google.com', 'https://mbtskoudsalg.com/images/person-stock-image-png.png'),
AppProfile('Fred', 'fred#google.com',
'https://media.istockphoto.com/photos/feeling-great-about-my-corporate-choices-picture-id507296326'),
AppProfile('Bera', 'bera#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('John', 'john#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Thomas', 'thomas#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Norbert', 'norbert#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
AppProfile('Marina', 'marina#flutter.io',
'https://upload.wikimedia.org/wikipedia/commons/7/7c/Profile_avatar_placeholder_large.png'),
];
class AppProfile {
final String name;
final String email;
final String imageUrl;
const AppProfile(this.name, this.email, this.imageUrl);
#override
bool operator ==(Object other) =>
identical(this, other) || other is AppProfile && runtimeType == other.runtimeType && name == other.name;
#override
int get hashCode => name.hashCode;
#override
String toString() {
return 'Profile{$name}';
}
}
// -------------------------------------------------
typedef ChipsInputSuggestions<T> = Future<List<T>> Function(String query);
typedef ChipSelected<T> = void Function(T data, bool selected);
typedef ChipsBuilder<T> = Widget Function(BuildContext context, ChipsInputState<T> state, T data);
class ChipsInput<T> extends StatefulWidget {
const ChipsInput({
Key key,
this.decoration = const InputDecoration(),
#required this.chipBuilder,
#required this.suggestionBuilder,
#required this.findSuggestions,
#required this.onChanged,
this.onChipTapped,
}) : super(key: key);
final InputDecoration decoration;
final ChipsInputSuggestions findSuggestions;
final ValueChanged<List<T>> onChanged;
final ValueChanged<T> onChipTapped;
final ChipsBuilder<T> chipBuilder;
final ChipsBuilder<T> suggestionBuilder;
#override
ChipsInputState<T> createState() => ChipsInputState<T>();
}
class ChipsInputState<T> extends State<ChipsInput<T>> implements TextInputClient {
static const kObjectReplacementChar = 0xFFFC;
Set<T> _chips = Set<T>();
List<T> _suggestions;
int _searchId = 0;
FocusNode _focusNode;
TextEditingValue _value = TextEditingValue();
TextInputConnection _connection;
String get text => String.fromCharCodes(
_value.text.codeUnits.where((ch) => ch != kObjectReplacementChar),
);
bool get _hasInputConnection => _connection != null && _connection.attached;
void requestKeyboard() {
if (_focusNode.hasFocus) {
_openInputConnection();
} else {
FocusScope.of(context).requestFocus(_focusNode);
}
}
void selectSuggestion(T data) {
setState(() {
_chips.add(data);
_updateTextInputState();
_suggestions = null;
});
widget.onChanged(_chips.toList(growable: false));
}
void deleteChip(T data) {
setState(() {
_chips.remove(data);
_updateTextInputState();
});
widget.onChanged(_chips.toList(growable: false));
}
#override
void initState() {
super.initState();
_focusNode = FocusNode();
_focusNode.addListener(_onFocusChanged);
}
void _onFocusChanged() {
if (_focusNode.hasFocus) {
_openInputConnection();
} else {
_closeInputConnectionIfNeeded();
}
setState(() {
// rebuild so that _TextCursor is hidden.
});
}
#override
void dispose() {
_focusNode?.dispose();
_closeInputConnectionIfNeeded();
super.dispose();
}
void _openInputConnection() {
if (!_hasInputConnection) {
_connection = TextInput.attach(this, TextInputConfiguration());
_connection.setEditingState(_value);
}
_connection.show();
}
void _closeInputConnectionIfNeeded() {
if (_hasInputConnection) {
_connection.close();
_connection = null;
}
}
#override
Widget build(BuildContext context) {
var chipsChildren = _chips
.map<Widget>(
(data) => widget.chipBuilder(context, this, data),
)
.toList();
final theme = Theme.of(context);
chipsChildren.add(
Container(
height: 32.0,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
text,
style: theme.textTheme.subhead.copyWith(
height: 1.5,
),
),
_TextCaret(
resumed: _focusNode.hasFocus,
),
],
),
),
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
//mainAxisSize: MainAxisSize.min,
children: <Widget>[
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: requestKeyboard,
child: InputDecorator(
decoration: widget.decoration,
isFocused: _focusNode.hasFocus,
isEmpty: _value.text.length == 0,
child: Wrap(
children: chipsChildren,
spacing: 4.0,
runSpacing: 4.0,
),
),
),
Expanded(
child: ListView.builder(
itemCount: _suggestions?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
return widget.suggestionBuilder(context, this, _suggestions[index]);
},
),
),
],
);
}
#override
void updateEditingValue(TextEditingValue value) {
final oldCount = _countReplacements(_value);
final newCount = _countReplacements(value);
setState(() {
if (newCount < oldCount) {
_chips = Set.from(_chips.take(newCount));
}
_value = value;
});
_onSearchChanged(text);
}
int _countReplacements(TextEditingValue value) {
return value.text.codeUnits.where((ch) => ch == kObjectReplacementChar).length;
}
#override
void performAction(TextInputAction action) {
_focusNode.unfocus();
}
void _updateTextInputState() {
final text = String.fromCharCodes(_chips.map((_) => kObjectReplacementChar));
_value = TextEditingValue(
text: text,
selection: TextSelection.collapsed(offset: text.length),
composing: TextRange(start: 0, end: text.length),
);
_connection.setEditingState(_value);
}
void _onSearchChanged(String value) async {
final localId = ++_searchId;
final results = await widget.findSuggestions(value);
if (_searchId == localId && mounted) {
setState(() => _suggestions = results.where((profile) => !_chips.contains(profile)).toList(growable: false));
}
}
}
class _TextCaret extends StatefulWidget {
const _TextCaret({
Key key,
this.duration = const Duration(milliseconds: 500),
this.resumed = false,
}) : super(key: key);
final Duration duration;
final bool resumed;
#override
_TextCursorState createState() => _TextCursorState();
}
class _TextCursorState extends State<_TextCaret> with SingleTickerProviderStateMixin {
bool _displayed = false;
Timer _timer;
#override
void initState() {
super.initState();
_timer = Timer.periodic(widget.duration, _onTimer);
}
void _onTimer(Timer timer) {
setState(() => _displayed = !_displayed);
}
#override
void dispose() {
_timer.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return FractionallySizedBox(
heightFactor: 0.7,
child: Opacity(
opacity: _displayed && widget.resumed ? 1.0 : 0.0,
child: Container(
width: 2.0,
color: theme.primaryColor,
),
),
);
}
}
I am trying to transfer the result of a query to a list but it is not working properly.
Below you will find the source code, it will be more clear.
I'm getting this error "type 'List' is not a subtype of type 'List'"
if the error is clear, I do not understand how to fix that.
I would like to get _allResults (contextName) into the list _context.
Many thanks for your help.
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:gtd_official_sharped_focused/models/context.dart';
import 'package:multi_select_flutter/multi_select_flutter.dart';
String taskImportant;
String taskUrgent;
class EngagePage_Sept_2021 extends StatefulWidget {
EngagePage_Sept_2021({Key key, }):super(key:key);//this.title}) : super(key: key);
// final String title;
#override
_EngagePage_Sept_2021State createState() => _EngagePage_Sept_2021State();
}
class _EngagePage_Sept_2021State extends State<EngagePage_Sept_2021> {
TextEditingController _searchController = TextEditingController();
Future resultsLoaded;
List _allResults = [];
List _resultsList = [];
#override
void initState() {
super.initState();
_selectedContext = _allResults;//_context;
_searchController.addListener(_onSearchChanged);
}
#override
void dispose(){
_searchController.removeListener(_onSearchChanged);
_searchController.dispose();
super.dispose();
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
resultsLoaded = getUsersListOfTasksStreamSnapshots();
}
_onSearchChanged() {
searchResultsList();
}
/*static List<Contexts> _monTest = [
Contexts(contextName: ),
];
*/
static List<Contexts> _context = [. //Here, I would prefer to store the data.docs
Contexts(id: '1', contextName: "name 1"),
Contexts(id: '2', contextName: "name 2"),
Contexts(id: '3', contextName: "name 3"),
];
// static List<Contexts> _test = _allResults;
final _itemsContext = _context
.map((context) => MultiSelectItem<Contexts>(context, context.contextName))
.toList();
List<Contexts> _selectedContext = [];
final _multiSelectKeyContext = GlobalKey<FormFieldState>();
final _multiSelectKeyStatus = GlobalKey<FormFieldState>();
final _multiSelectKey4 = GlobalKey<FormFieldState>();
final _multiSelectKeyTime = GlobalKey<FormFieldState>();
searchResultsList() {
var showResults = [];
if(_searchController.text != "") {
for(var taskSnapshot in _allResults){
var title = Contexts.fromSnapshot(taskSnapshot).contextName.toLowerCase();
if(title.contains(_searchController.text.toLowerCase())) {
showResults.add(taskSnapshot);
}
}
} else {
showResults = List.from(_allResults);
}
setState(() {
_resultsList = showResults;
});
}
getUsersListOfTasksStreamSnapshots() async {
var data = await FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('contexts')
.get();
setState(() {
_allResults = data.docs;
print(_allResults);
});
searchResultsList();
return 'complete';
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('TEST CHIP'),
),
drawer: MyMenu(),
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(20),
child: Column(
children: <Widget>[
//################################################################################################
// CONTEXT - MultiSelectBottomSheetField with validators
//################################################################################################
MultiSelectBottomSheetField<Contexts>(
key: _multiSelectKeyContext,
initialChildSize: 0.7,
maxChildSize: 0.95,
title: Text("Context"),
buttonText: Text("Context",style: TextStyle(fontSize: 18),),
items: _itemsContext,
searchable: true,
validator: (values) {
if (values == null || values.isEmpty) {
return "";
}
List<String> names = values.map((e) => e.contextName).toList();
return null;
},
onConfirm: (values) {
setState(() {
_selectedContext = values;
});
_multiSelectKeyContext.currentState.validate();
},
chipDisplay: MultiSelectChipDisplay(
onTap: (item) {
setState(() {
_selectedContext.remove(item);
});
_multiSelectKeyContext.currentState.validate();
},
),
),
SizedBox(height: 40),
//################################################################################################
// STATUS - MultiSelectBottomSheetField with validators
//################################################################################################
],
),
],
),
),
),
);
}
}
Model context
import 'package:cloud_firestore/cloud_firestore.dart';
class Contexts {
String id;
String contextName;
Contexts({
this.id,
this.contextName,
});
// formatting for upload to Firebase when creating the trip
Map<String, dynamic> toJson() =>
{
'context_Name': contextName,
};
//creating a Task object from a firebase snapshot
Contexts.fromSnapshot(DocumentSnapshot snapshot) :
id = snapshot.id,
contextName = snapshot['context_Name'];
}
I tried to migrate the no null safety code to null safety and I ended up with errors. I want to get autocomplete location of places in Flutter and display details on the tapped place.
Screenshots of errors:
The code:
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');
}
}
}
The solution is probably this:
builder: (context, AsyncSnapshot<List<Suggestion>> snapshot)
instead of this:
builder: (context, snapshot)
then you can do something like:
List<Suggestion>? suggestions = snapshot.data;
if ( suggestions != null && suggestions.length > 0) {
I have a ListView.builder that builds a certain amount of widgets depending on user input. Each widget has their own specific name and has a DropDownMenu. I save this value with the corresponding name of the widget. It saves it correctly. However, when I try and read the data and create a new list from it, this error appears: [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method '[]' was called on null.
'course' is a list. I am using the shared preferences import. When you tap the flat button, it should build the new list, but it is not. Could you explain to me why this is not working please?
This is code in the main app.
void main() {
runApp(Hemis());
}
class Hemis extends StatefulWidget {
#override
_HemisState createState() => _HemisState();
}
class _HemisState extends State<Hemis> {
_read() async {
final prefs = await SharedPreferences.getInstance();
for(int i = 0; i < course.length; i++) {
listMarks[i].name = course[i].name;
listMarks[i].mark = prefs.getInt(course[i].name) ?? 0;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
itemCount: course.length,
itemBuilder: (context, index) {
return ModuleListItem(
name: '${course[index].name}',
credits: course[index].credits,
);
},
),
FlatButton(
onPressed: () {
_read();
for(int i = 0; i < course.length; i++) {
print('${listMarks[i].name}: ${listMarks[i].mark}');
}
},
),
],
),
)
)
);
}
}
The widget that is being built.
final percentage = List<String>.generate(100, (i) => "$i");
class ModuleListItem extends StatefulWidget {
const ModuleListItem ({ Key key, this.name, this.credits }): super(key: key);
final String name;
final int credits;
#override
_ModuleListItemState createState() => _ModuleListItemState();
}
class _ModuleListItemState extends State<ModuleListItem> {
String dropdownValue;
bool isSwitched = false;
_save() async {
final prefs = await SharedPreferences.getInstance();
final key = '${widget.name}';
final value = int.parse(dropdownValue);
prefs.setInt(key, value);
print('saved $value');
}
#override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
DropdownButton<String>(
value: dropdownValue,
icon: Icon(Icons.keyboard_arrow_down),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
items: percentage.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
Switch(
value: isSwitched,
onChanged: (value) {
setState(() {
isSwitched = value;
if(isSwitched == true) {
_save();
}
print(isSwitched);
});
},
),
],
),
);
}
}