I'm trying to pass the data of a List for another screen but i don't know how to do that.
I've tried using constructors and using correctly null-safety but the app returned this error: Null check operator used on a null Value. And i'm a long time already trying to solve this problem but i don't know how. What's the benefits of null safety? Here's the code:
Code 1
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:mood3/telas/RecebeDados.dart';
import 'package:mood3/telas/listas.dart';
import 'package:path_provider/path_provider.dart';
import 'package:toast/toast.dart';
import 'Diary.dart';
class Escrita extends StatefulWidget {
List? lista = [];
Escrita({this.lista});
#override
State<Escrita> createState() => _EscritaState();
}
class _EscritaState extends State<Escrita> {
Future<File>_getFile()async{
final diretorio = await getApplicationDocumentsDirectory();
return File("${diretorio.path}/dados.json");
}
_salvar() async {
var arquivo = await _getFile();
Map<String,dynamic> tarefa = Map();
tarefa["titulo"] = "Ir ao mercado";
tarefa["realizada"] = false;
widget.lista!.add(tarefa);
String dados = json.encode(widget.lista);
arquivo.writeAsString(dados);
}
_ler() async {
try{
final arquivo = await _getFile();
return arquivo.readAsString();
}catch(e){
return null;
}
}
#override
void initState() {
super.initState();
_ler().then((dados){
setState(() {
widget.lista = json.decode(dados);
});
});
}
TextEditingController _controllerTarefa = TextEditingController();
#override
Widget build(BuildContext context) {
ToastContext().init(context);
return Scaffold(
body: Center(
child: SingleChildScrollView(
child:Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: TextField(
keyboardType: TextInputType.multiline,
controller: _controllerTarefa,
decoration: InputDecoration(
hintText: "Diary Title",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.blueGrey,
),
)),
Padding(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 16),
child: TextField(
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: InputDecoration(
hintText: "Diary Text",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.blueGrey,
),
),),
],
),),
),
}
),
]
),
);
}
}
Code 2:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:mood3/telas/Account.dart';
import 'package:mood3/telas/Diary.dart';
import 'package:mood3/telas/Friends.dart';
import 'package:mood3/telas/Graph.dart';
import 'package:mood3/telas/Home.dart';
import 'package:mood3/telas/Music.dart';
import 'package:mood3/telas/PlayerDoVideo.dart';
import 'package:mood3/telas/Videos.dart';
import 'package:mood3/telas/animacao.dart';
import 'package:mood3/telas/escrita.dart';
import 'package:persistent_bottom_nav_bar_v2/persistent-tab-view.dart';
class RecebeDados extends StatefulWidget {
#override
State<RecebeDados> createState() => _RecebeDadosState();
}
class _RecebeDadosState extends State<RecebeDados> {
Escrita flista = Escrita();
late List? list = flista.lista;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: list!.length,
itemBuilder: (context, index){
return ListTile(
title: Text( list![index]['titulo'] ),
);
}
),
)
],
),
floatingActionButton: SpeedDial(
animatedIcon: AnimatedIcons.menu_close,
backgroundColor: Colors.black,
icon: CupertinoIcons.pencil,
overlayColor: Colors.black,
children: [
Text ("teste")
]
),
);
}
}
In this case you are creating "lista" as a List that could be null (List?), but also you initialize it with value: a empty list ( [] )
List? lista = [];
But when you call the constructor of Escrita, you are giving a value to the lista variable.
Widget myWidget = Escrita(); /// lista is null
lista is null, because you are not indicating a value in the constructor.
If you want a default value this is the correct way:
Escrita({this.lista = []});
You are receiving errors because you are using list!.length and list![index]['titulo'], and using the ! you are asecuring that the list is not null, but yes, its null! So thats the reason because you are receiving the error.
You can solve your problem in this way:
class Escrita extends StatefulWidget {
List lista;
Escrita({this.lista=[]});
#override
State<Escrita> createState() => _EscritaState();
}
Or you can do this:
class Escrita extends StatefulWidget {
List lista;
Escrita({required this.lista});
#override
State<Escrita> createState() => _EscritaState();
}
Or
class Escrita extends StatefulWidget {
List lista;
Escrita(this.lista);
#override
State<Escrita> createState() => _EscritaState();
}
Related
I'm new to flutter and have a memory leak in my program. I was wondering if anyone jumped out to anyone about my code as to why it might be causing a memory leak. I was hoping I made some sort of newbie mistake that someone could call out. I commented out my listviewbuilder row widget and replaced it with simple text, and that seemed to lessen the memory leak. So it seems like it might be row related, but I don't understand how.
I am not scrolling or anything like that I just fire up the memory profiler. It shows that there is an ever-increasing size in the filtered node, but does not say why.
I went back through my changesets and found some code that seemed to be causing the issues. Specifically the memory leak went away when I rmeoveLoadingAnimationWidget:
AnimatedOpacity(
opacity: isUpdating ? 1.0 : 0.0,
duration: isUpdating
? const Duration(milliseconds: 0)
: const Duration(milliseconds:500),
child: Row(
children: [
LoadingAnimationWidget.fourRotatingDots(
color: Colors.black12,
size: 20,
),
SizedBox(
width: 3,
),
Text(
"Saving...",
style: TextStyle(color: Colors.grey.shade600),
),
],
),
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:save_extra_money/main.dart';
import 'package:save_extra_money/shared/loading/LoadingIndicator.dart';
import 'package:save_extra_money/transactions/TransactionUpdaterProvider.dart';
import 'package:save_extra_money/transactions/TransactionsLoadingStates.dart';
import 'package:save_extra_money/transactions/transactionGridRow.dart';
import 'package:http/http.dart' as http;
import '../Shared/ColorContainer.dart';
import '../model/Transaction.dart';
import '../model/webCallInputsAndOutputs/RetrieveTransactionsResults.dart';
import '../shared/globals/GlobalUi.dart';
import 'TransactionsViewModel.dart';
class Transactions extends ConsumerStatefulWidget {
Transactions({Key? key}) : super(key: key);
#override
TransactionsState createState() => TransactionsState();
}
class TransactionsState extends ConsumerState<Transactions> {
ScrollController? controller;
#override
void initState() {
super.initState();
controller = ScrollController()..addListener(_scrollListener);
}
#override
void dispose() {
if (controller != null) {
controller!.removeListener(_scrollListener);
}
super.dispose();
}
void _scrollListener() {
if (controller == null) {
return;
}
// print(controller.position.extentAfter);
if (controller!.position.extentAfter < 300) {
ref.read(transactionsViewModel.notifier).addMorePagedData();
}
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
var transactionUpdater = ref.watch(transactionUpdaterProvider);
var viewModel = ref.watch(transactionsViewModel);
viewModel.loadInitialData();
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(viewModel.getTitle()),
backgroundColor: GlobalUI.appBar,
centerTitle: true,
),
body: ColorContainer(
size: size,
child: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child:
getInitialLayout(viewModel, transactionUpdater),
),
),
);
}
Widget getInitialLayout(TransactionsViewModel viewModel,
TransactionUpdaterProvider transactionUpdater){
if(viewModel.state == TransactionsLoadingStates.InitialLoad){
return LoadingIndicator(text: 'Transactions...');
}
else if(viewModel.state == TransactionsLoadingStates.InitialError) {
return Text('Error: ${viewModel.errorText}');
}
else{
return ListView.builder(
controller: controller,
itemCount: viewModel.totalCount,
itemBuilder: (BuildContext context, int index) {
if(index==viewModel.transactions.length){
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5)
),
color: GlobalUI.rowBackground,
),
child: LoadingAnimationWidget.fourRotatingDots(
color: Colors.black12,
size: 60,
),
);
}
if(index>viewModel.transactions.length){
return SizedBox(height: 0,width: 0,);
}
var transaction = viewModel.transactions[index];
return Text(transaction.id);
/*
return TransactionGridRow(
transaction: transaction,
isUpdating: transactionUpdater.isUpdating(transaction.id),
);
*/
});
}
}
}
You should not make api calls in your widget build method
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:save_extra_money/main.dart';
import 'package:save_extra_money/shared/loading/LoadingIndicator.dart';
import 'package:save_extra_money/transactions/TransactionUpdaterProvider.dart';
import 'package:save_extra_money/transactions/TransactionsLoadingStates.dart';
import 'package:save_extra_money/transactions/transactionGridRow.dart';
import 'package:http/http.dart' as http;
import '../Shared/ColorContainer.dart';
import '../model/Transaction.dart';
import '../model/webCallInputsAndOutputs/RetrieveTransactionsResults.dart';
import '../shared/globals/GlobalUi.dart';
import 'TransactionsViewModel.dart';
class Transactions extends ConsumerStatefulWidget {
Transactions({Key? key}) : super(key: key);
#override
TransactionsState createState() => TransactionsState();
}
class TransactionsState extends ConsumerState<Transactions> {
ScrollController? controller;
#override
void initState() {
super.initState();
controller = ScrollController()..addListener(_scrollListener);
viewModel.loadInitialData(); // You can call your async methods in initstate
}
#override
void dispose() {
if (controller != null) {
controller!.removeListener(_scrollListener);
}
super.dispose();
}
void _scrollListener() {
if (controller == null) {
return;
}
// print(controller.position.extentAfter);
if (controller!.position.extentAfter < 300) {
ref.read(transactionsViewModel.notifier).addMorePagedData();
}
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
var transactionUpdater = ref.watch(transactionUpdaterProvider);
var viewModel = ref.watch(transactionsViewModel);
viewModel.loadInitialData(); // Here is the method you should not call here
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(viewModel.getTitle()),
backgroundColor: GlobalUI.appBar,
centerTitle: true,
),
body: ColorContainer(
size: size,
child: ClipRRect(
borderRadius: BorderRadius.circular(5.0),
child:
getInitialLayout(viewModel, transactionUpdater),
),
),
);
}
Widget getInitialLayout(TransactionsViewModel viewModel,
TransactionUpdaterProvider transactionUpdater){
if(viewModel.state == TransactionsLoadingStates.InitialLoad){
return LoadingIndicator(text: 'Transactions...');
}
else if(viewModel.state == TransactionsLoadingStates.InitialError) {
return Text('Error: ${viewModel.errorText}');
}
else{
return ListView.builder(
controller: controller,
itemCount: viewModel.totalCount,
itemBuilder: (BuildContext context, int index) {
if(index==viewModel.transactions.length){
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(5),
bottomRight: Radius.circular(5)
),
color: GlobalUI.rowBackground,
),
child: LoadingAnimationWidget.fourRotatingDots(
color: Colors.black12,
size: 60,
),
);
}
if(index>viewModel.transactions.length){
return SizedBox(height: 0,width: 0,);
}
var transaction = viewModel.transactions[index];
return Text(transaction.id);
/*
return TransactionGridRow(
transaction: transaction,
isUpdating: transactionUpdater.isUpdating(transaction.id),
);
*/
});
}
}
}
In addition to batuhand, Avoid this:
if(index>viewModel.transactions.length){
return SizedBox(height: 0,width: 0,);
}
Each time index is greater than length it will built a useless SizedBox.
Why do you use "viewModel.totalCount" and "viewModel.transactions.length"? It should be the same.
And if you compare index and length add a minus to length
if(index==viewModel.transactions.length-1)
I have declared a variable as global and I need to access a value inside a function using this global variable.
I have tried this in many ways but it is not working.
How is this possible?
Go through my code in below
I need to get value of 'savedvalue' which is inside future function 'saveddata', in value1(gobal varable)
import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/key.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LoginScreen extends StatelessWidget {
LoginScreen({Key? key}) : super(key: key);
#override
final inputtext = TextEditingController();
String? value1;
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: Container(
width: 300,
child: Column(children: [
TextField(
controller: inputtext,
decoration: InputDecoration(
border: OutlineInputBorder(), label: Text('Enter your name')),
),
ElevatedButton.icon(
onPressed: () {
savedata(context);
// print(value1);
},
icon: Icon(Icons.arrow_right),
label: Text('click Here'))
]),
),
)),
);
}
Future<void> savedata(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('Name', inputtext.text);
final savedValue = prefs.getString('Name');
print(savedValue);
// value1=savedValue.toString();
if (savedValue == 'jibi') {
Navigator.of(context).pushNamed('Screen_2');
// print('Working');
}
this.value1 = savedValue;
}
}
function savedata is asynchronous but onPressed is not, hence print(value1) may execute before savedata is completed.
try:
onPressed: () async {
await savedata(context);
print(value1);
},
Imagine I have 2 textfields that a user can input a 24 hour time in the format 1400 for example. Textfield 1 is the start time of a shift and textfield 2 is the end time. Ideally I'd like the duration between the two times to be displayed in a text widget when either textfields onSubmit is called or even if 1 of the textfields loses focus. I'm able to calculate and display the duration of 2 hard coded strings using the intl package with x.difference(y) but I'm struggling to get to this point with the textfields. Thanks for your help.
edit thinking about it after the initial post, the need for textfields isn't 100% required. the two times to compare could come from something like a datetime picker instead. what matters is that i've tried textfields, datetime picker and the time picker but can't arrive a a solution.
import 'package:flutter/material.dart';
class ActivityLog extends StatefulWidget {
#override
_ActivityLogState createState() => _ActivityLogState();
}
class _ActivityLogState extends State<ActivityLog> {
TextEditingController _controller1 = TextEditingController();
TextEditingController _controller2 = TextEditingController();
String duration = '0000';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Activity Log'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _controller1,
maxLength: 4,
maxLines: 1,
keyboardType: TextInputType.datetime,
decoration: InputDecoration(
labelText: 'Start time',
hintText: 'hhmm',
counterText: '',
),
),
TextField(
controller: _controller2,
maxLength: 4,
maxLines: 1,
keyboardType: TextInputType.datetime,
decoration: InputDecoration(
labelText: 'Finish time',
hintText: 'hhmm',
counterText: '',
),
),
Text('Duration: $duration'),
/*
can't figure out how to get the input from the textfields in the format of
HHmm (hours and minutes) and calculate the duration which is to be displayed
in the text widget above
*/
],
),
),
);
}
}
See if below works for you. Clearly I am not doing any validation, but this could be the start and you can build from that. Here is alsp an image of how it looks here.
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _controller1 = TextEditingController();
final _controller2 = TextEditingController();
var _result = "";
String addColon(String s) {
return s.substring(0, 2) + ":" + s.substring(2);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SafeArea(
child: Column(
children: [
TextField(controller: _controller1),
TextField(controller: _controller2),
ElevatedButton(
onPressed: () {
var _d1 =
DateFormat.Hm().parse(addColon(_controller1.text));
var _d2 =
DateFormat.Hm().parse(addColon(_controller2.text));
setState(() {
_result =
'${_d2.difference(_d1).inSeconds.toString()} seconds';
});
},
child: const Text("Diff")),
const SizedBox(height: 50),
Text(_result),
],
),
),
),
);
}
}
Program is saying : The argument type String Function(String) can't be assigned to the parameter type String?
Function(String?)?`. İn the validator are and under line , I Wrote
error.
import 'package:asdasd/Validation/Student_Validator.dart';
import 'package:asdasd/models/Student.dart';
import 'package:flutter/material.dart';
class StudentAdd extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _StudentAddState();
}
}
class _StudentAddState extends State with StudentValidationMixin {
var student = Student.withoutinfo();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Yeni öğrenci ekleme "),
),
body: Container(
margin: EdgeInsets.all(20.0),
child: Form(
child: Column(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
labelText: "Öğrencinin Adı", hintText: "Musa Develi"),
validator: validateFirstName,//eror
onSaved: (String value){//eror
student.firstName=value;//eror
},
),
],
),
),
),
);
}
}
class StudentValidation {
}
i'm guessing validateFirstName is a function, right? You have to make your validator function the same as the validator,
String? Function(String?)
String is not the same as String?, the first means the value cannot be null, the second means it can be null.
this happened because null-safety is enabled
I have a list of objects that I can display in a ListView. Now I wanted to implement a search feature and only display the search result. When I try to do it using onChanged on TextField(or even Controller) it doesn't work. I tried to debug and he gets the list updated correctly but he doesn't update the Widget. But when I removed the onChanged and added a button and then called the same method that I was calling on onChanged everything worked.
The goal is to update the widget as the user writes in the text field.
I would be happy to get some help
My full code :
import 'package:flutter/material.dart';
import 'package:hello_fridge/single_ingredient_icon.dart';
import 'package:string_similarity/string_similarity.dart';
import 'entities/ingredient.dart';
class IngredientsContainer extends StatefulWidget {
const IngredientsContainer({Key? key}) : super(key: key);
#override
_IngredientsContainerState createState() => _IngredientsContainerState();
}
class _IngredientsContainerState extends State<IngredientsContainer> {
late List<Ingredient> ingredients;
final searchController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
searchController.dispose();
super.dispose();
}
void updateResults(String newValue) {
if (newValue.isEmpty) {
ingredients = Ingredient.getDummyIngredients();
} else {
print("new Value = $newValue");
ingredients = this.ingredients.where((ing) {
double similarity =
StringSimilarity.compareTwoStrings(ing.name, newValue);
print("$similarity for ${ing.name}");
return similarity > 0.2;
}).toList();
ingredients.forEach((element) {
print("found ${element.name}");
});
}
setState(() {});
}
Widget _searchBar(List<Ingredient> ingredients) {
return Row(
children: <Widget>[
IconButton(
splashColor: Colors.grey,
icon: Icon(Icons.restaurant),
onPressed: null,
),
Expanded(
child: TextField(
controller: searchController,
onChanged: (newValue) {
updateResults(newValue);
},
cursorColor: Colors.black,
keyboardType: TextInputType.text,
textInputAction: TextInputAction.go,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 15),
hintText: "Search..."),
),
),
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: IconButton(
icon: Icon(
Icons.search,
color: Color(0xff9ccc65),
),
onPressed: () {
updateResults(searchController.text);
},
),
),
],
);
}
#override
void initState() {
this.ingredients = Ingredient.getDummyIngredients();
super.initState();
}
#override
Widget build(BuildContext context) {
return Material(
child: Column(children: [
Expanded(flex: 1, child: _searchBar(this.ingredients)),
Expanded(flex: 4, child: IngredientsGrid(this.ingredients))
]),
);
}
}
class IngredientsGrid extends StatelessWidget {
List<Ingredient> ingredients;
IngredientsGrid(this.ingredients);
List<Widget> _buildIngredients() {
return this.ingredients.map((ing) => SingleIngredientIcon(ing)).toList();
}
// const IngredientsGrid({
// Key? key,
// }) : super(key: key);
#override
Widget build(BuildContext context) {
this.ingredients.forEach((ing) => print(ing.name! + ","));
return ListView(
children: <Widget>[
GridView.count(
crossAxisCount: 4,
// physics: NeverScrollableScrollPhysics(),
// to disable GridView's scrolling
shrinkWrap: true,
// You won't see infinite size error
children: _buildIngredients()),
// ...... other list children.
],
);
}
}
Moreover, I keep getting this Warning :
"Changing the content within the composing region may cause the input method to behave strangely, and is therefore discouraged. See https://github.com/flutter/flutter/issues/78827 for more details".
Visiting the linked GitHub page wasn't helpful
The problem is that while you are correctly filtering the list but your TextController is not getting assigned any value.
So, no value is getting assigned to your TextField as the initial value and hence the list again filters to have the entire list.
To solve this just assign the TextController the newValue like this.
void updateResults(String newValue) {
if (newValue.isEmpty) {
ingredients = Ingredient.getDummyIngredients();
} else {
print("new Value = $newValue");
ingredients = this.ingredients.where((ing) {
double similarity =
StringSimilarity.compareTwoStrings(ing.name, newValue);
print("$similarity for ${ing.name}");
return similarity > 0.2;
}).toList();
ingredients.forEach((element) {
print("found ${element.name}");
});
}
// change
searchController = TextEditingController.fromValue(
TextEditingValue(
text: newValue,
),
);
setState(() {});
}
If it throws an error then remove final from the variable declaration, like this :
var searchController = TextEditingController();