I've build a Custemized List. Now I include a Checkbox and if I would checked or unchecked , the following error was thrown: 'setState() called in constructor'
class Lists extends StatefulWidget{
#override
_List createState() => _List();
}
class _List extends State<Lists> {
bool checkedvalue = true;
#override
Widget build(BuildContext context) {
return futureBuilder();
}
Widget futureBuilder(){
var futureBuilder = new FutureBuilder(
future: rest.fetchPost(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return new Text('loading...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return listBuilder(context, snapshot);
}
}
);
return new Scaffold(
body: futureBuilder,
);
}
Widget listBuilder(BuildContext context, AsyncSnapshot snapshot) {
List<rest.Status> values = snapshot.data;
if (values == null || values.length == 0){
return null;
}
int items = values.length;
return ListView.builder(
itemCount: items,
itemBuilder: (BuildContext context, int index) {
String statusText;
Image image ;
Uint8List bytes;
if(statusList.globalStatus != null){
for(int i=0;i< statusList.globalStatus.length; i++){
if(values[index].statusID == statusList.globalStatus[i].id){
if(statusList.globalStatus[i].kurzform != null){
statusText = statusList.globalStatus[i].kurzform;
}else{
statusText = statusList.globalStatus[i].kurzform;
}
if (statusList.globalStatus[i].icon != null){
bytes = base64Decode(statusList.globalStatus[i].icon);
image = new Image.memory(bytes) ;
}
}
if(image== null || statusText == null){
statusText= 'Kein Status';
image= new Image.asset('assets/null.png');
}
}
}
return new Container(
decoration: new BoxDecoration(
border: Border(top: BorderSide(
color: Colors.black26,
width: 1
)
)
),
child:Column(
children: <Widget>[
CustomListItemTwo(
statusText: statusText,
status:image,
materialNR: values[index].uArtText,
material: values[index].untersuchungsMaterialName,
probenArt: values[index].probenart,
eingansdatum: values[index].eingangsdatumText,
patient: values[index].vorname + ' ' + values[index].nachname ,
geburtsdatum: values[index].geburtstagText ,
),
Checkbox(
value: checkedvalue ,
onChanged: (bool newValue) =>
setState(() {
checkedvalue = newValue;
})
),
]
),
);
}
);
}
}
I/flutter ( 5067): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter ( 5067): The following assertion was thrown while handling a gesture:
I/flutter ( 5067): setState() called in constructor: _List#9044e(lifecycle state: created, no widget, not mounted)
I/flutter ( 5067): This happens when you call setState() on a State object for a widget that hasn't been inserted into
I/flutter ( 5067): the widget tree yet. It is not necessary to call setState() in the constructor, since the state is
I/flutter ( 5067): already assumed to be dirty when it is initially created.
My code below is not testet.
There is somewhat a concept error in your code. You should NOT fetch anything inside your build method!
If you put a print e.g. "building..." in your build-method (as I did below) you will see why. The build method is called more than you might think. So you are calling a WebService or whatever more then once, the response will come more then once. Actually the setState() method will trigger a build.
If you want to pull something at the beginning use the initState() method. This method will be called once when the state was created. Use Variables for the state of the call and react to it in the build Method (as said before setState() will trigger a rebuild).
I refactored your code a bit, having this concept in mind, your switch/checkbox problem probably will be gone.
Also please take a look how to use Futures https://api.flutter.dev/flutter/dart-async/Future-class.html
class Lists extends StatefulWidget {
#override
_List createState() => _List();
}
class _List extends State<Lists> {
bool checkedvalue = true;
bool loading = true;
AsyncSnapshot asyncSnapshot = null;
#override
void initState() {
futureBuilder();
super.initState();
}
#override
Widget build(BuildContext context) {
print("building...");
if(asyncSnapshot != null && asyncSnapshot.hasError){
return Text("Error : ${asyncSnapshot.error}");
}
return (loading) ? Text("LOADING") : listBuilder(context, asyncSnapshot);
}
void futureBuilder() async {
rest.fetchPost().then((snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
setState(() {
loading = true;
});
break;
default:
if (snapshot.hasError) {
setState(() {
loading = false;
});
} else {
setState(() {
loading = false;
asyncSnapshot = snapshot;
});
}
}
});
}
.....
Related
I have List array which is based on flutter multi_image_picker: ^4.7.14 dependency. How can I convert List Asset Array to List File Array?
This is the code
class _ConvertImageState extends State<ConvertImage> {
List<Asset> images = List<Asset>();
#override
void initState() {
super.initState();
}
Future<void> pickImages() async {
List<Asset> resultList = List<Asset>();
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 20,
enableCamera: true,
selectedAssets: images,
materialOptions: MaterialOptions(
selectCircleStrokeColor: "white",
actionBarTitle: "Select Images",
allViewTitle: "All Images",
actionBarColor: "#3b3b3b",
actionBarTitleColor: "white",
statusBarColor: '#bbbbbb',
selectionLimitReachedText: "You can select minimum 3 and maximum 20 images",
),
);
} on Exception catch (e) {
print(e);
}
setState(() {
images = resultList;
});
}
#override
Widget build(BuildContext context) {......}
}
Can I do it with a loop? Please, anyone can tell me the correct way?
Here I find an answer. This is my working code. I think it is correct.
class _ConvertImageState extends State<ConvertImage> {
List<Asset> images = List<Asset>();
List<File> fileImageArray = [];
#override
void initState() {
super.initState();
}
Future<void> pickImages() async {
List<Asset> resultList = List<Asset>();
try {
resultList = await MultiImagePicker.pickImages(
maxImages: 20,
enableCamera: true,
selectedAssets: images,
materialOptions: MaterialOptions(
selectCircleStrokeColor: "white",
actionBarTitle: "Select Images",
allViewTitle: "All Images",
actionBarColor: "#3b3b3b",
actionBarTitleColor: "white",
statusBarColor: '#bbbbbb',
selectionLimitReachedText: "You can select minimum 3 and maximum 20 images",
),
);
} on Exception catch (e) {
print(e);
}
setState(() {
images = resultList;
putToFileArray();
});
}
void putToFileArray(){
fileImagesArray.clear();
images.forEach((imageAsset) async {
final filePath =
await FlutterAbsolutePath.getAbsolutePath(imageAsset.identifier);
File tempFile = File(filePath);
if (tempFile.existsSync()) {
fileImageArray.add(tempFile);
}
});
}
#override
Widget build(BuildContext context) {......}
}
FlutterAbsolutePathClass (Dependency)
flutter_absolute_path: ^1.0.6
Link: https://pub.dev/packages/flutter_absolute_path
To Print Array
onPressed: () {
print(fileImageArray.toString());
};
I want to connect to my firebase database as now called "realtime database".
My Apps is running smoothly, but I am getting following error if I want to start the method in my app through a button:
======== Exception caught by widgets library =======================================================
The following NoSuchMethodError was thrown building LoadDataFromFireStore(dirty, state: LoadDataFromFireStoreState#20451):
The getter 'keys' was called on null.
Receiver: null
Tried calling: keys
The relevant error-causing widget was:
LoadDataFromFireStore file:///C:/Users/Nutzer/AndroidStudioProjects/tennis_sv_schwaig/lib/widget/kalender.dart:17:13
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1 LoadDataFromFireStoreState._showCalendar (package:tennis_sv_schwaig/widget/kalender.dart:58:34)
#2 LoadDataFromFireStoreState.build (package:tennis_sv_schwaig/widget/kalender.dart:49:13)
#3 StatefulElement.build (package:flutter/src/widgets/framework.dart:4744:28)
#4 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4627:15)
...
====================================================================================================
I do not know if theres is a collision with a keyForm in an other method before. Could this be possible?
Here is the code from the method:
import 'dart:math';
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';
import 'package:intl/intl.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(kalender());
class kalender extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: LoadDataFromFireStore(),
);
}
}
class LoadDataFromFireStore extends StatefulWidget {
#override
LoadDataFromFireStoreState createState() => LoadDataFromFireStoreState();
}
class LoadDataFromFireStoreState extends State<LoadDataFromFireStore> {
DataSnapshot querySnapshot;
dynamic data;
List<Color> _colorCollection;
#override
void initState() {
_initializeEventColor();
getDataFromDatabase().then((results) {
setState(() {
if (results != null) {
querySnapshot = results;
}
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: _showCalendar(),
);
}
_showCalendar() {
if (querySnapshot != null) {
List<Meeting> collection;
var showData = querySnapshot.value;
Map<dynamic, dynamic> values = showData;
List<dynamic> key = values.keys.toList();
if (values != null) {
for (int i = 0; i < key.length; i++) {
data = values[key[i]];
collection ??= <Meeting>[];
final Random random = new Random();
collection.add(Meeting(
eventName: data['Subject'],
isAllDay: false,
from: DateFormat('dd/MM/yyyy HH:mm:ss').parse(data['StartTime']),
to: DateFormat('dd/MM/yyyy HH:mm:ss').parse(data['EndTime']),
background: _colorCollection[random.nextInt(9)]));
}
} else {
return Center(
child: CircularProgressIndicator(),
);
}
return SafeArea(
child: Column(
children: [
Container(
height: 400,
child: SfCalendar(
view: CalendarView.month,
initialDisplayDate: DateTime(2020, 4, 5, 9, 0, 0),
dataSource: _getCalendarDataSource(collection),
monthViewSettings: MonthViewSettings(showAgenda: true),
),
),
RaisedButton(onPressed: () {
final dbRef = FirebaseDatabase.instance.reference().child("CalendarData");
dbRef.push().set({
"StartTime": '07/04/2020 07:00:00',
"EndTime": '07/04/2020 08:00:00',
"Subject":'NewMeeting',
}).then((_) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Successfully Added')));
}).catchError((onError) {
print(onError);
});
}, child: Text("Add")),
RaisedButton(onPressed: () {
final dbRef = FirebaseDatabase.instance.reference().child("CalendarData");
dbRef.remove();
}, child: Text("Delete")),
],
));
}
}
void _initializeEventColor() {
this._colorCollection = new List<Color>();
_colorCollection.add(const Color(0xFF0F8644));
_colorCollection.add(const Color(0xFF8B1FA9));
_colorCollection.add(const Color(0xFFD20100));
_colorCollection.add(const Color(0xFFFC571D));
_colorCollection.add(const Color(0xFF36B37B));
_colorCollection.add(const Color(0xFF01A1EF));
_colorCollection.add(const Color(0xFF3D4FB5));
_colorCollection.add(const Color(0xFFE47C73));
_colorCollection.add(const Color(0xFF636363));
_colorCollection.add(const Color(0xFF0A8043));
}
}
MeetingDataSource _getCalendarDataSource([List<Meeting> collection]) {
List<Meeting> meetings = collection ?? <Meeting>[];
return MeetingDataSource(meetings);
}
class MeetingDataSource extends CalendarDataSource {
MeetingDataSource(List<Meeting> source) {
appointments = source;
}
#override
DateTime getStartTime(int index) {
return appointments[index].from;
}
#override
DateTime getEndTime(int index) {
return appointments[index].to;
}
#override
bool isAllDay(int index) {
return appointments[index].isAllDay;
}
#override
String getSubject(int index) {
return appointments[index].eventName;
}
#override
Color getColor(int index) {
return appointments[index].background;
}
}
getDataFromDatabase() async {
var value = FirebaseDatabase.instance.reference();
var getValue = await value.child('CalendarData').once();
return getValue;
}
class Meeting {
Meeting({this.eventName, this.from, this.to, this.background, this.isAllDay});
String eventName;
DateTime from;
DateTime to;
Color background;
bool isAllDay;
}
Hope its enough and anybody could help. Thanks a lot!
I tried to display a timer ( format dd HH mm ss ) to count the time between each actions (button action for exemple ). And need to work even the app is close and rebuild. Currently I load a string date I saved with sharedpreference when I pressed a button who represent the time when I pressed the button. I format all time decimal to compare and display time difference. I think it's not beautifull, not what I search, and I don't succeded to display clock in the format (dd HH mm ss). If someone have a more simple exemple :)
load_records_pulsion() async{
/*var current_time = DateFormat('yyyy-MM-dd HH').format(DateTime.now());*/
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
RegExp regExp = new RegExp( //Here is the regex time pulsion
r"([12]\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])",
);
last_pulsion = (prefs.getString('last_pulsion'))??0;
var match = regExp.firstMatch("$last_pulsion");
annees = match.group(1); // hh:mm
mois = match.group(2); // hh:mm
jours = match.group(3); // hh:mm
int annees_int = int.tryParse("$annees") ;
int mois_int = int.tryParse("$mois") ;
int jours_int = int.tryParse("$jours") ;
print("$annees_int");
print("$mois_int");
print("$jours_int");
final last_pulsion2 = DateTime(annees_int, mois_int, jours_int);
final date_now = DateTime.now();
difference_pulsion = date_now.difference(last_pulsion2).inDays;
if(difference_pulsion==0){
difference_pulsion ="";
prefix_pulsion ="Aujourd'hui";
}else{
prefix_pulsion ="jours";
}
});
}
Also I tried this code, it's OK the timer is increase when I call the function, but I don't want datenow, I just need to start with zero time
int _start = 0;
void startTimer() {
_start=0;
var now = new DateTime.now();
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) => setState(() {
{
chrono = now.add(new Duration(seconds: _start));
_start = _start + 1;
}
}));
}
Edit: I found this solution but have some lifecycle error, and if I close the app, I loose the timer.
Stopwatch stopwatch = new Stopwatch();
void rightButtonPressed() {
setState(() {
if (stopwatch.isRunning) {
stopwatch.reset();
} else {
stopwatch.reset();
stopwatch.start();
}
});
}
#override
Widget build(BuildContext context)
{
...
new Container(height: 80.0,
child: new Center(
child: new TimerText(stopwatch: stopwatch),
)),
...
class TimerText extends StatefulWidget {
TimerText({this.stopwatch});
final Stopwatch stopwatch;
TimerTextState createState() => new TimerTextState(stopwatch: stopwatch);
}
class TimerTextState extends State<TimerText> {
Timer timer;
final Stopwatch stopwatch;
TimerTextState({this.stopwatch}) {
timer = new Timer.periodic(new Duration(milliseconds: 30), callback);
}
void callback(Timer timer) {
if (stopwatch.isRunning) {
setState(() {
});
}
}
#override
Widget build(BuildContext context) {
final TextStyle timerTextStyle = const TextStyle(fontSize: 50.0, fontFamily: "Open Sans");
String formattedTime = TimerTextFormatter.format(stopwatch.elapsedMilliseconds);
return new Text(formattedTime, style: timerTextStyle);
}
}
class TimerTextFormatter {
static String format(int milliseconds) {
int seconds = (milliseconds / 1000).truncate();
int minutes = (seconds / 60).truncate();
int hours = (minutes / 60).truncate();
int days = (hours / 24).truncate();
String minutesStr = (minutes % 60).toString().padLeft(2, '0');
String secondsStr = (seconds % 60).toString().padLeft(2, '0');
String hoursStr = (hours % 60).toString().padLeft(2, '0');
String daysStr = (days % 24).toString().padLeft(2, '0');
return "$daysStr:$hoursStr:$minutesStr:$secondsStr";
}
}
If you want the counter to persist after closing the app, there is no way around saving the value somewhere (like shared preferences).
Using dateTime.toIso8601String() and DateTime.parse() will make the saving and loading less ugly.
To calculate the passed time you can use DateTime.now().difference(lastButtonPressed)
There should be a function to format Duration (https://api.flutter.dev/flutter/intl/DateFormat/formatDurationFrom.html) but it's not implemented yet. I found one here: Formatting a Duration like HH:mm:ss
Here is a little example:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutterfly/SharedPrefs.dart';
class TestWidget extends StatefulWidget {
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
DateTime _lastButtonPress;
String _pressDuration;
Timer _ticker;
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Time since button pressed"),
Text(_pressDuration),
RaisedButton(
child: Text("Press me"),
onPressed: () {
_lastButtonPress = DateTime.now();
_updateTimer();
sharedPreferences.setString("lastButtonPress",_lastButtonPress.toIso8601String());
},
)
],
),
);
}
#override
void initState() {
super.initState();
final lastPressString = sharedPreferences.getString("lastButtonPress");
_lastButtonPress = lastPressString!=null ? DateTime.parse(lastPressString) : DateTime.now();
_updateTimer();
_ticker = Timer.periodic(Duration(seconds:1),(_)=>_updateTimer());
}
#override
void dispose() {
_ticker.cancel();
super.dispose();
}
void _updateTimer() {
final duration = DateTime.now().difference(_lastButtonPress);
final newDuration = _formatDuration(duration);
setState(() {
_pressDuration = newDuration;
});
}
String _formatDuration(Duration duration) {
String twoDigits(int n) {
if (n >= 10) return "$n";
return "0$n";
}
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return "${twoDigits(duration.inHours)}:$twoDigitMinutes:$twoDigitSeconds";
}
}
For simplicity i initialized shared preferences in the main method in global scope.
I would like to show a calendar not as a dialog. And I'll like the use to be able to select date interval as in the screenshot.
I would recommend you not to reinvent the wheel and pick one of the community calendar widgets (like that one), but in case you need a custom solution, you may start with something really simple. For example, if you need to pick a range you may just take a grid and a few buttons like that:
import 'package:flutter/material.dart';
class CalendarPage extends StatefulWidget {
final String title;
CalendarPage({Key key, this.title}) : super(key: key);
#override
State<StatefulWidget> createState() => _CalendarPageState();
}
class _CalendarPageState extends State<CalendarPage> {
int _left = -1;
int _right = -1;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: GridView.count(
crossAxisCount: 7,
children: List.generate(31, (index) {
return Container(
decoration: BoxDecoration(
border: Border.all(width: 2.0, color: Colors.black38),
color: _isInBounds(index)
? Colors.yellow[100]
: Colors.transparent,
borderRadius: const BorderRadius.all(const Radius.circular(8.0)),
),
margin: const EdgeInsets.all(2.0),
child: FlatButton(
onPressed: () => _handleTap(index),
child: Text('${index + 1}',
style: Theme.of(context).textTheme.body2,
textAlign: TextAlign.center)));
}),
));
}
void _handleTap(index) {
setState(() {
if (_left == -1)
_left = index;
else if (_right == -1) _right = index;
});
}
bool _isInBounds(int index) => _left <= index && index <= _right;
}
UI: https://flutter.io/tutorials/layout/
Selecting a range: https://www.didierboelens.com/2018/07/range-slider/
You'll learn a lot from these. Good luck!
In my programme I've a large array of strings(say 1600) which I want to show as a CheckBox list. The array is actually the location of all the songs in one's PC, and thus can gradually be bigger. I don't wanna use ListView<String> as the CheckBox list is more efficient and above all visually better for my purpose. I'm currently doing the below :
private void listAll() {
songs = MediaManager.getAllSongs();
VBox vb = new VBox();
vb.setSpacing(5);
vb.getStyleClass().add("background");
if (songs != null) {
Service s = new Service() {
#Override
protected Task createTask() {
Task t = new Task() {
#Override
protected Object call() throws Exception {
for (String song : songs) {
addSong(song, vb);
c++;
updateMessage(c+" songs");
}
return null;
}
};
t.messageProperty().addListener((obs,o,n)->{
count.setText(n);
});
return t;
}
};
s.start();
ScrollPane sp = new ScrollPane(vb);
getChildren().add(sp);
}
}
private void addSong(String n, Pane p) {
String toAdd = "";
Media m = new Media(Paths.get(n).toUri().toString());
if (m.getMetadata().get("title") == null || !(m.getMetadata().get("title").equals(""))) {
toAdd = m.getSource().split("/")[m.getSource().split("/").length - 1].replace("%20", " ").replace(".mp3", "");
} else {
toAdd = ((String) m.getMetadata().get("title"));
}
SongBox s = new SongBox(toAdd);
s.setUserData(n);
p.getChildren().add(s);
}
class SongBox extends CheckBox {
public SongBox(String t) {
this();
setText(t);
}
public SongBox() {
super();
setOnAction((ActionEvent evt) -> {
if (isSelected()) {
if (!playNow.isVisible()) {
playNow.setVisible(true);
}
path = (String) getUserData();
selected.add((String) getUserData());
} else {
selected.remove((String) getUserData());
if (selected.size() == 0) {
playNow.setVisible(false);
}
}
});
}
}
First of all, that is not showing the complete array. Whenever I'm going back and returning to it, the number of songs get changed. Secondly, the whole UI is getting sluggish(sometimes also hanging my PC). Moreover, I can't cancel the Service when I've gone to the previous window, as it's always returning false. Anyone have a better approach?