Flutter sharedPreferences initialization - flutter

i want to initialize my sharedPref object before app loading.I tried init func etc. but always on the secreen appears "Instance of Future" text then my sharedPref object initialization end then getStringList method is executed properly.I mean i don't want to see "Instance of Future".What should i do?
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
_MyHomePageState().createSharedObject();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
_MyHomePageState().createSharedObject();
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String baslik, mesaj;
List<String> kaydedilenNotlar = [];
List<String> kaydedilenBasliklar = [];
List<String> deneme = [];
SharedPreferences sharedPreferences;
Future createSharedObject() async {
sharedPreferences = await SharedPreferences.getInstance();
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
backgroundColor: Colors.yellowAccent[100],
title: Text(
"Yeni Not",
textAlign: TextAlign.center,
),
content: Container(
height: 250,
child: Column(
children: [
Expanded(
child: TextField(
onChanged: (value) {
setState(() {
baslik = value;
});
},
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Başlık",
hintStyle: TextStyle(
color: Colors.black,
fontStyle: FontStyle.italic),
),
),
),
TextField(
onChanged: (value) {
setState(() {
mesaj = value;
});
},
maxLines: 5,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: "Not",
),
),
TextButton(
onPressed: () async {
kaydedilenNotlar.add(mesaj);
kaydedilenBasliklar.add(baslik);
sharedPreferences =
await SharedPreferences.getInstance();
/* sharedPreferences.setStringList(
"not", kaydedilenNotlar);
sharedPreferences.setStringList(
"baslik", kaydedilenBasliklar);*/
},
child: Text(
"Kaydet",
style: TextStyle(fontSize: 18),
))
],
),
),
);
});
},
),
appBar: AppBar(
centerTitle: true,
title: Text("AJANDA"),
bottom: TabBar(
tabs: [
Tab(
child: Text("Yapılacak"),
),
Tab(
child: Text("Yapılıyor"),
),
Tab(
child: Text("Yapıldı"),
)
],
),
),
body: TabBarView(
children: [
Center(
child: Text(
"${sharedPreferences == null ? createSharedObject() : sharedPreferences.getStringList("not")}")),
Center(child: Text("2. sayfa")),
Center(child: Text("3. sayfa")),
],
),
),
);
}
}

The reason for this is that your createSharedObject() function is a Future. And in the lines
child: Text("${sharedPreferences == null ? createSharedObject() : sharedPreferences.getStringList("not")}")),
you are using that Future value and not awaiting for it.
So, if you want to await for the actual value, you'll need a FutureBuilder.
FutureBuilder<String>(
future: your_future_function,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState==ConnectionState.done){
return Text(snapshot.data);
} else {
return Text('Loading');
}
});
I must say I think there are better ways to deal with the loading of values from SharedPreferences, but for the sake of simplicity, the solution above should work.

Related

Deleting Item out of List, Listview.build shows wrong data

I have a Stateful widget that i pass a list to (for example 2 items).
After I delete an item, the widget should rebuild itself.
Unfortunately, the deleted item is still displayed and the other one is not.
When I re-enter the widget, the correct item is loaded.
There is a similar problem List not updating on deleting item
but maybe someone can explain me what i did wrong and why provider is helping me here instead of setState?
My code is:
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:trip_planner/util/dialog_box.dart';
import 'package:trip_planner/util/previewUrl.dart';
class BookingPage extends StatefulWidget {
final List toDoList;
BookingPage({
super.key,
required this.toDoList,
});
#override
State<BookingPage> createState() => _BookingPageState();
}
class _BookingPageState extends State<BookingPage> {
//text controller
final _controller = TextEditingController();
final _database = FirebaseDatabase.instance.ref();
//Liste is an example what i have in my list
List toDoList2 = [
["https://www.booking.com/Share-Rnv2Kf", true],
["https://www.booking.com/Share-3hKQ0r", true],
];
void initState(){
super.initState();
}
void deleteTask(int index){
setState(() {
widget.toDoList.removeAt(index);
});
//DatabaseReference _testRef = _database.child("Hotel:");
//_testRef.set(widget.toDoList.toString());
}
//save new Item
void saveNewItem(){
setState(() {
widget.toDoList.add([_controller.text, false]);
//DatabaseReference _testRef = _database.child("Hotel:");
//_testRef.set(widget.toDoList.toString());
_controller.clear();
});
Navigator.of(context).pop();
}
void createNewItem(){
showDialog(
context: context,
builder: (context){
return DialogBox(
controller: _controller,
onSave: saveNewItem,
onCancel: () => Navigator.of(context).pop(),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Booking Seiten'),
elevation: 0,
),
floatingActionButton: FloatingActionButton(
onPressed: createNewItem,
child: Icon(Icons.add),
),
body: ListView.builder(
itemCount: widget.toDoList.length,
itemBuilder: (context, index){
return PreviewUrl(
url2: widget.toDoList[index][0],
deleteFunction: (context) => setState(() => deleteTask(index)),
);
},
),
);
}
}
i thought setState does the same thing as when i re-enter the widget, but it doesn't.
import 'package:any_link_preview/any_link_preview.dart';
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:url_launcher/url_launcher.dart';
class PreviewUrl extends StatelessWidget {
final String url2;
//Function(bool?)? onChanged;
Function(BuildContext)? deleteFunction;
PreviewUrl({
super.key,
required this.url2,
required this.deleteFunction,
//required this.onChanged,
});
Future openBrowserURL({
required String url,
bool inApp = false,
}) async {
if(await canLaunch(url)){
await launch(
url,
forceSafariVC: inApp, //iOS
forceWebView: inApp, //Android
enableJavaScript: true, //Android
);
}
}
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(1.0),
child: Slidable(
endActionPane: ActionPane(
motion: StretchMotion(),
children: [
SlidableAction(
onPressed: deleteFunction,
icon: Icons.delete,
backgroundColor: Colors.red.shade300,
borderRadius: BorderRadius.circular(12),
)
],
),
child: Container(
child: AnyLinkPreview.builder(
link: url2,
itemBuilder: (context, metadata, imageProvider) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (imageProvider != null)
GestureDetector(
onTap: () async {
final url = url2;
openBrowserURL(url: url, inApp: true);
},
child: Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.width *0.25,
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12)),
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
),
),
),
),
Container(
width: double.infinity,
color: Theme.of(context).primaryColor.withOpacity(0.6),
padding: const EdgeInsets.symmetric(
vertical: 10, horizontal: 15),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (metadata.title != null)
Text(
metadata.title!,
maxLines: 1,
style:
const TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 5),
if (metadata.desc != null)
Text(
metadata.desc!,
maxLines: 1,
style: Theme.of(context).textTheme.bodySmall,
),
Text(
metadata.url ?? url2,
maxLines: 1,
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
),
),
);
}
}
If you run the simplified version of your code in DartPad - it will work:
import 'package:flutter/material.dart';
const Color darkBlue = Color.fromARGB(255, 18, 32, 47);
List toDoList = [
["Button 1", true],
["Button 2", true],
];
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: BookingPage(toDoList: toDoList),
),
),
);
}
}
class BookingPage extends StatefulWidget {
final List toDoList;
const BookingPage({
super.key,
required this.toDoList,
});
#override
State<BookingPage> createState() => _BookingPageState();
}
class _BookingPageState extends State<BookingPage> {
//Liste is an example what i have in my list
List toDoList2 = [
["Button 1", true],
["Button 2", true],
];
#override
void initState() {
super.initState();
}
void deleteTask(int index) {
setState(() {
widget.toDoList.removeAt(index);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Booking Seiten'),
elevation: 0,
),
body: ListView.builder(
itemCount: widget.toDoList.length,
itemBuilder: (context, index) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.lightBlue,
padding: const EdgeInsets.all(12),
textStyle: const TextStyle(fontSize: 22),
),
child: Text(widget.toDoList[index][0]!),
onPressed: () => setState(() => deleteTask(index)),
);
},
),
);
}
}
Which tells me that the problem is your PreviewUrl. My guess is - it is a statful widget, and when the tree rebuilds - it will link the old State object to the first item.
Using Keys might help, something like:
return PreviewUrl(
key: ObjectKey(widget.toDoList[index]),
url2: widget.toDoList[index][0],
deleteFunction: (context) => setState(() => deleteTask(index)),
);

Provider returning null when rebuilding Flutter app

I'm pretty new to flutter and I'm trying to make a login system using providers. It seems to be working when I test the login. But when I rebuild the app the provider returns a null value. Any help would be appreciated.
The screen to check for employee data. If it exist it should redirect to the home page. And if it doesn't, it should redirect to the login authenticate page
Landing Page
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class Landing extends StatefulWidget {
#override
_LandingState createState() => _LandingState();
}
class _LandingState extends State<Landing> {
//AuthService auth = new AuthService();
#override
Widget build(BuildContext context) {
Future<Employee> getuserdata() => Employee_preferences().getEmployee();
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => AuthService(),
),
ChangeNotifierProvider(
create: (_) => Employee_Provider(),
)
],
child: MaterialApp(
title: 'ClockServe',
theme: ThemeData(primarySwatch: Colors.blue),
home: FutureBuilder(
future: getuserdata(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return CircularProgressIndicator();
default:
if (snapshot.hasError) {
return Text('Error:${snapshot.error}');
} else if (snapshot.data.empId == null) {
return AuthenticatePage();
} else {
return HomePage(emp: snapshot.data);
}
}
}),
routes: {
'/navigatorPage': (context) => NavigatorPage(),
'/homePage': (context) => HomePage(),
'/authenticate': (context) => AuthenticatePage(),
'/attendancePage': (context) => AttendanceScanner()
},
),
);
}
}
The homepage. The page will hold employee information. Landing page is correctly redirecting to this page but for some reason the provider is returning null
HomePage
class HomePage extends StatefulWidget {
final Employee emp;
const HomePage({Key key, this.emp}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
//to do: add back end
//use futurebuilder to return user object
//using futureprovider to get snapshot data of user object from database
#override
Widget build(BuildContext context) {
Employee emp = Provider.of<Employee_Provider>(context).emp;
print(emp.empEmail);
return Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () async {
Employee_preferences().removeEmployee();
Navigator.pushReplacementNamed(context, '/authenticate');
},
label: Text(
'Log Out',
style: TextStyle(color: Colors.white),
),
icon: Icon(
Icons.logout,
color: Colors.white,
),
)
],
title: Text('ClockServe'),
centerTitle: true,
),
//button to pop qr scanner camera
//after scanning a qr code it should parse the json array
//into a method, the method will take that as parameter.
//method should send http request check in the auth dart
floatingActionButton: FloatingActionButton.extended(
label: Text('Check In'),
icon: Icon(Icons.camera_alt),
onPressed: () => navigateToScanPage(context),
),
// floatingActionButton: FloatingActionButton(
// onPressed: () {},
// child: Icon(Icons.alarm_on),
// ),
body: SingleChildScrollView(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(emp.empFirstName ?? 'emp first name'),
],
),
),
),
);
}
}
Future navigateToScanPage(context) async {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AttendanceScanner()));
}
Code for login page just in case if it's relevant.
Login Page
class LoginPage extends StatefulWidget {
final Function toggleView;
LoginPage({this.toggleView});
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formKey = GlobalKey<FormState>();
String email = '';
String password = '';
String error = '';
bool loading = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
ElevatedButton.icon(
onPressed: () {
widget.toggleView();
},
label: Text('Register'),
icon: Icon(Icons.person_add),
)
],
title: Text('Login'),
),
body: Container(
padding: EdgeInsets.all(30),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
WelcomeHeader(),
SizedBox(
height: 10,
),
TextFormField(
validator: (value) => value.isEmpty ? 'Enter email' : null,
onChanged: (val) {
setState(() => email = val);
},
decoration: decorationBox.copyWith(hintText: 'Email'),
),
SizedBox(
height: 20,
),
TextFormField(
validator: (value) => value.isEmpty ? 'Enter password' : null,
onChanged: (val) {
setState(() => password = val);
},
obscureText: true,
decoration: decorationBox.copyWith(hintText: 'Password'),
),
SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () async {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
AuthService auth = new AuthService();
final Future<Map<String, dynamic>> successMsg =
auth.empLogin(email, password);
successMsg.then((response) {
if (response['status']) {
Employee emp = response['employee'];
print(emp);
Provider.of<Employee_Provider>(context, listen: false)
.setEmp(emp);
Navigator.pushReplacementNamed(context, '/homePage');
}
});
}
},
child: Text('Log In'),
),
SizedBox(
height: 20.0,
),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 20.0),
)
],
),
),
),
),
);
}
}
class WelcomeHeader extends StatelessWidget {
const WelcomeHeader({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
Text(
'Welcome To ClockServe',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28.0,
),
),
Divider(
height: 20,
thickness: 2,
),
Text(
'Enter your credentials to login',
style: TextStyle(fontStyle: FontStyle.italic),
),
],
),
);
}
}

Flutter, prevent the keyboard from showing once the time is entered

I would like to prevent the keyboard from showing once the time is entered, how could I do it?
Updated
I tried using FocusScope.of(context).unfocus(); and it works for the first try, but it doesn't for the second one. It's kinda weird. Look at this.
The first works, the second doesn't work, but the third works, also noticed that the keyboard show up even before than the TimePicker is showed. (Sorry for my bad english)
this is the code;
TextEditingController _startTime = TextEditingController();
Widget _createTimePicker(String text, TextEditingController controller) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
validator: (String value) {
if (value.isEmpty) {
return 'Es necesario especificar una hora.';
}
},
controller: controller,
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onTap: () {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
FocusScope.of(context).unfocus();
},
)
],
),
);
}
TimeOfDay _time = TimeOfDay.now().replacing(minute: 30);
void onTimeChanged(TimeOfDay newTime) {
setState(() {
_time = newTime;
_startTime.text = _time.format(context);
});
}
You can copy paste run 2 full code below
Solution 1: Quick fix for current code
You can use Future.delayed and FocusManager.instance.primaryFocus.unfocus
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
await Future.delayed(Duration(milliseconds: 50), () {
FocusManager.instance.primaryFocus.unfocus();
});
})
Solution 2: Assume you do not need keyboard to show up all the time
You can use GestureDetector wrap TextFormField and set enable to false
GestureDetector(
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
},
child: TextFormField(
enabled: false,
working demo 1
working demo 2
full code 1
import 'package:day_night_time_picker/lib/daynight_timepicker.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _startTime = TextEditingController();
final _formKey = GlobalKey<FormState>();
Widget _createTimePicker(String text, TextEditingController controller) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
//enabled: false,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: controller,
decoration: InputDecoration(
errorStyle: TextStyle(color: Colors.red),
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
await Future.delayed(Duration(milliseconds: 200), () {
FocusManager.instance.primaryFocus.unfocus();
});
})
],
),
);
}
TimeOfDay _time = TimeOfDay.now().replacing(minute: 30);
void onTimeChanged(TimeOfDay newTime) {
setState(() {
_time = newTime;
_startTime.text = _time.format(context);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_createTimePicker("", _startTime),
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {}
},
child: Text('Submit'),
)
],
),
),
),
);
}
}
full code 2
import 'package:day_night_time_picker/lib/daynight_timepicker.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController _startTime = TextEditingController();
final _formKey = GlobalKey<FormState>();
Widget _createTimePicker(String text, TextEditingController controller) {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
GestureDetector(
onTap: () async {
Navigator.of(context).push(
showPicker(
context: context,
value: _time,
onChange: onTimeChanged,
is24HrFormat: true,
),
);
},
child: TextFormField(
enabled: false,
validator: (String value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: controller,
decoration: InputDecoration(
errorStyle: TextStyle(color: Colors.red),
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
),
)
],
),
);
}
TimeOfDay _time = TimeOfDay.now().replacing(minute: 30);
void onTimeChanged(TimeOfDay newTime) {
setState(() {
_time = newTime;
_startTime.text = _time.format(context);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_createTimePicker("", _startTime),
ElevatedButton(
onPressed: () {
if (_formKey.currentState.validate()) {}
},
child: Text('Submit'),
)
],
),
),
),
);
}
}
on the callback function of the time selection dismiss the keyboard
FocusScope.of(context).unfocus();
if this field is only editable through the time picker, you can make the text field read-only by setting its attribute
readOnly: true
if the field can be edited by keyboard, then you can await for the dialog result and then call
FocusScope.of(context).requestFocus(new FocusNode());
After set time to TextInput or on confirm time select, call this FocusScope.of(context).unfocus() function.
// update your function
void onTimeChanged(TimeOfDay newTime) {
FocusScope.of(context).unfocus();
...
}
and,
TextFormField(
readOnly: true,
...
)

Flutter how to get user input using text form in show dialog?

I'm trying to get the user input to change the title using a text form in show dialog but it seems the state is rebuilding whenever the keyboard shows/closes, my code is working before, but when I did flutter upgrade to v1.17 it's not working anymore. I've been stuck here for a couple of days now and I don't know what's wrong with my code or what error might be causing it, I can only see "getSelectedText on inactive InputConnection" and "mSecurityInputMethodService is null" in the debug console, please help.
Here's a sample of my code:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
final TextEditingController titleController = new TextEditingController();
final GlobalKey<FormState> _keyDialogForm = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
titleController.text = 'Hello';
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Column(
children: <Widget>[
Text(titleController.text),
SizedBox(
height: 50,
),
FlatButton(
color: Colors.redAccent,
onPressed: () {
showTitleDialog();
},
child: Text(
'Show Dialog',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
))
],
),
));
}
Future showTitleDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Form(
key: _keyDialogForm,
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.ac_unit),
),
maxLength: 8,
textAlign: TextAlign.center,
onSaved: (val) {
titleController.text = val;
},
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter Title Name';
}
return null;
},
)
],
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
if (_keyDialogForm.currentState.validate()) {
_keyDialogForm.currentState.save();
Navigator.pop(context);
}
},
child: Text('Save'),
color: Colors.blue,
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel')),
],
);
});
}
}
You can copy paste run full code below
You can call setState in onSaved
code snippet
onSaved: (val) {
titleController.text = val;
setState(() {});
},
working demo
full code
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
final TextEditingController titleController = new TextEditingController();
final GlobalKey<FormState> _keyDialogForm = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
titleController.text = 'Hello';
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: Center(
child: Column(
children: <Widget>[
Text(titleController.text),
SizedBox(
height: 50,
),
FlatButton(
color: Colors.redAccent,
onPressed: () {
showTitleDialog();
},
child: Text(
'Show Dialog',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
))
],
),
));
}
Future showTitleDialog() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Form(
key: _keyDialogForm,
child: Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
icon: Icon(Icons.ac_unit),
),
maxLength: 8,
textAlign: TextAlign.center,
onSaved: (val) {
titleController.text = val;
setState(() {});
},
autovalidate: true,
validator: (value) {
if (value.isEmpty) {
return 'Enter Title Name';
}
return null;
},
)
],
),
),
actions: <Widget>[
FlatButton(
onPressed: () {
if (_keyDialogForm.currentState.validate()) {
_keyDialogForm.currentState.save();
Navigator.pop(context);
}
},
child: Text('Save'),
color: Colors.blue,
),
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Cancel')),
],
);
});
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Test(),
);
}
}

A non-null String must be provided to a Text widget. Error

I get this error when I run the app. I have followed a youtube tutorial but in my case, the error A non-null String must be provided to a Text widget always comes up. I've already tried multiple things but nothing happened. I don't know what else I can write... but I can't post the question without giving more detailed information.
How can I solve this problem?
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Colors.blue,
accentColor: Colors.orange
),
home: MyApp(),
));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
List todos = List();
String input = "";
createTodos() {
DocumentReference documentReference =
Firestore.instance.collection("MyTodos").document(input);
//Map
Map<String, String> todos = {"todoTitle": input};
documentReference.setData(todos).whenComplete(() {
print("$input created");
});
}
deleteTodos() {
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My ToDos'),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Add ToDo', style: TextStyle(fontWeight: FontWeight.bold),),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15)
),
content: TextField(
onChanged: (String value) {
input = value;
},
),
actions: <Widget>[
FlatButton(
onPressed: () {
createTodos();
Navigator.of(context).pop();
},
child: Text('Add'))
],
);
});
},
child: Icon(
Icons.add,
color: Colors.white,
),
),
body: StreamBuilder(
stream: Firestore.instance.collection("MyTodos").snapshots(),
builder: (context, snapshots){
if(snapshots.data == null) return CircularProgressIndicator();
return ListView.builder(
shrinkWrap: true,
itemCount: snapshots.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot documentSnapshot = snapshots.data.documents[index];
return Dismissible(
key: Key(index.toString()),
child: Card(
elevation: 4,
margin: EdgeInsets.all(8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)
),
child: ListTile(
title: Text(documentSnapshot["todoTitle"]),
trailing: IconButton(
icon: Icon(
Icons.delete),
color: Colors.red,
onPressed: (){
setState(() {
todos.removeAt(index);
});
} ),
),
));
});
}),
);
}
}
Your documentSnapshot["todoTitle"] is returning null and you shouldn't provide any null value to Text widget. So, a solution would be to use something like
Text(documentSnapshot["todoTitle"] ?? "No title found")