Countdown timer date and time format in Flutter - flutter

I'm making a Countdown timer, but the problem is, how can I make it realize the difference from AM to PM if it is a 24h clock? For instance, I have put the wakeUpTime inside the code to be tomorrow morning at 10. And if the time I'm asking this question is let's say 17:55h, I get a countdown to only 4 hours and 5min remaining, but it is supposed to say 16hours and 5min left. How can I achieve this? Here is the code below:
DateTime formatedDate = DateTime.parse(wakeUpTime);
int estimatedTimeLeft = formatedDate.millisecondsSinceEpoch; // needed date to countdown to
StreamBuilder(
stream: Stream.periodic(Duration(seconds: 1), (i) => i),
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
DateFormat format = DateFormat("hh:mm:ss");
int timeNow= DateTime
.now()
.millisecondsSinceEpoch;
Duration remaining = Duration(milliseconds: estimatedTimeLeft - timeNow);
var dateString = '${remaining.inHours}:${format.format(
DateTime.fromMillisecondsSinceEpoch(remaining.inMilliseconds))}';
print(dateString);
return Container(
child: Text(dateString),);
});
Did I format the Date and time wrong, or is it something else?
Thanks in advance for your help!

Below is a minimal code for testing, you can just copy and paste the code below, should work fine. Just input the endDate in DateTime format.
import 'dart:async';
import 'package:flutter/material.dart';
class DebugErrorClass extends StatefulWidget {
DebugErrorClass({
Key? key,
}) : super(key: key);
#override
_DebugErrorClassState createState() => _DebugErrorClassState();
}
class _DebugErrorClassState extends State<DebugErrorClass> {
String remainingTime = "";
Timer? _timer;
StreamController<String> timerStream = StreamController<String>.broadcast();
final endDate = DateTime.now().add(Duration(days: 5)); // the date, time u set
final currentDate = DateTime.now();
#override
void initState() {
prepareData();
super.initState();
}
#override
void dispose() {
try{
if (_timer != null && _timer!.isActive) _timer!.cancel();
}catch(e){
print(e);
}
super.dispose();
}
prepareData(){
final difference = daysBetween(currentDate, endDate);
print(difference);
print('difference in days');
// get remaining time in second
var result = Duration(seconds: 0);
result = endDate.difference(currentDate);
remainingTime = result.inSeconds.toString(); // convert to second
// remainingTime = '10'; // change this value to test for min function
}
int daysBetween(DateTime from, DateTime to) {
from = DateTime(from.year, from.month, from.day);
to = DateTime(to.year, to.month, to.day);
return (to.difference(from).inHours / 24).round();
}
String dayHourMinuteSecondFunction(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, "0");
String days = twoDigits(duration.inDays);
String twoDigitHours = twoDigits(duration.inHours.remainder(24));
String twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
String twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
return days+' '+"Days"+' '+twoDigitHours+' '+"Hours"+' '+twoDigitMinutes+' '+"Minutes"+' '+twoDigitSeconds+"Seconds";
}
Widget dateWidget(){
return StreamBuilder<String>(
stream: timerStream.stream,
initialData: "0",
builder: (cxt, snapshot) {
const oneSec = Duration(seconds: 1);
if (_timer != null && _timer!.isActive) _timer!.cancel();
_timer = Timer.periodic(oneSec, (Timer timer) {
try {
int second = int.tryParse(remainingTime)?? 0;
second = second - 1;
if (second < -1) return;
remainingTime = second.toString();
if (second == -1) {
timer.cancel();
print('timer cancelled');
}
if(second >= 0){
timerStream.add(remainingTime);
}
} catch (e) {
print(e);
}
});
String remainTimeDisplay = "-";
try {
int seconds = int.parse(remainingTime);
var now = Duration(seconds: seconds);
remainTimeDisplay = dayHourMinuteSecondFunction(now);
} catch (e) {
print(e);
}
print(remainTimeDisplay);
return Text(remainTimeDisplay, textAlign: TextAlign.center,);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
alignment: Alignment.center,
width: double.infinity,
height: double.infinity,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
dateWidget(),
],
),
),
),
),
);
}
}

Related

Where to prevent re-animation of ListView.builder items when scrolling back to previously animated items?

I have a listview in which the text of items are animated when they first appear - and when they reappear after enough scrolling. When the list grows to certain size and the user scrolls back far enough items are animated again - presumably they've been removed from the widget tree and are now being re-inserted and thus get re-initiated etc. I want to prevent this from happening so that they only animate the first time they appear.
I think this means I need to have state stored somewhere per item that keeps track and tells the individual items whether they should animate on them being built or not. I am not sure where to put and how to connect that though, partly because it seems to overlap between presentation and business logic layers. I think perhaps it should be a variable in the list items contained in the list object that the listview builder is constructing from - or should it somehow be in the actual widgets in the listview?
class _StockListViewBuilderState extends State<StockListViewBuilder> with asc_alertBar {
final ScrollController _scrollController = ScrollController();
late double _scrollPosition;
late double _maxScrollExtent;
late bool isThisTheEnd = false;
_scrollListener() async {
setState(() {
_scrollPosition = _scrollController.position.pixels;
_maxScrollExtent = _scrollController.position.maxScrollExtent;
});
if (!isThisTheEnd && _scrollPosition / _maxScrollExtent > 0.90) {
isThisTheEnd = true;
if (widget.stockListicle.getIsRemoteEmpty()) {
alertBar('No more items available', /* null,*/ context: context);
} else {
await widget.stockListicle.fetch(numberToFetch: 5);
}
}
if (isThisTheEnd && _scrollPosition / _maxScrollExtent <= 0.90) {
isThisTheEnd = false;
}
}
#override
void initState() {
super.initState();
late String? userFullName = GetIt.I.get<Authenticate>().user?.fullName;
developer.log('Authenticated user $userFullName', name: '_StockListViewBuilderState');
developer.log("init ", name: "_StockListViewBuilderState ");
int listCount;
_scrollController.addListener(_scrollListener);
WidgetsBinding.instance.addPostFrameCallback((_) async {
//developer.log("stckLtcl init pf con ");
listCount = widget.stockListicle.items.length;
if (listCount < 10 && !widget.stockListicle.getIsRemoteEmpty()) {
try {
await widget.stockListicle.fetch(numberToFetch: 10);
} catch (e) {
super.setState(() {
//developer.log("Can't load stock:$e");
alertBar(
"Couldn't load from the internet.",
context: context,
backgroundColor: Colors.purple,
);
});
}
}
});
WidgetsBinding.instance.addPostFrameCallback((_) async {
final ConnectionNotifier connectionNotifier = context.read<ConnectionNotifier>();
if (connectionNotifier.isConnected() != true) {
await connectionNotifier.check();
if (connectionNotifier.isConnected() != true) {
alertBar("Please check the internet connection.", context: context);
}
}
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.vertical,
controller: _scrollController,
shrinkWrap: true,
key: widget.theKey,
itemCount: widget.stockListicle.items.length + 1,
itemBuilder: (context, index) {
if (index <= widget.stockListicle.items.length - 1) {
return InkWell(
onTap: (() => Navigator.pushNamed(
context,
'/stocks/stock',
arguments: ScreenArguments(widget.stockListicle.items[index] as Stock),
)),
child: StockListItem(
stock: widget.stockListicle.items[index] as Stock,
));
} else {
return LoadingItemNotifier(
isLoading: widget.stockListicle.getIsBusyLoading(),
);
}
},
);
}
}
//...
Currently StockListItem extends StatelessWidget and returns a 'ListTile' which as its title parameter has ...title: AnimatedText(textContent: stock.title),...
I was trying to keep track of first-time-animation inside AnimatedText widget until I realized from an OOP & Flutter perspective, it's probably wrong place...
class AnimatedText extends StatefulWidget {
final bool doShowMe;
final String textContent;
final Duration hideDuration;
final double durationFactor;
const AnimatedText({
Key? key,
this.doShowMe = true,
this.textContent = '',
this.hideDuration = const Duration(milliseconds: 500),
this.durationFactor = 1,
}) : super(key: key);
#override
State<AnimatedText> createState() => _AnimatedTextState();
}
class _AnimatedTextState extends State<AnimatedText> with SingleTickerProviderStateMixin {
late AnimationController _appearanceController;
late String displayText;
late String previousText;
late double durationFactor;
late Duration buildDuration = Duration(
milliseconds: (widget.textContent.length / 15 * widget.durationFactor * 1000).round());
#override
void initState() {
super.initState();
developer.log('init ${widget.textContent}', name: '_AnimatedTextState');
displayText = '';
previousText = widget.textContent;
_appearanceController = AnimationController(
vsync: this,
duration: buildDuration,
)..addListener(
() => updateText(),
);
if (widget.doShowMe) {
_doShowMe();
}
}
void updateText() {
String payload = widget.textContent;
int numCharsToShow = (_appearanceController.value * widget.textContent.length).ceil();
if (widget.doShowMe) {
// make it grow
displayText = payload.substring(0, numCharsToShow);
// developer.log('$numCharsToShow / ${widget.textContent.length} ${widget.textContent}');
} else {
// make it shrink
displayText = payload.substring(payload.length - numCharsToShow, payload.length);
}
}
#override
void didUpdateWidget(AnimatedText oldWidget) {
super.didUpdateWidget(oldWidget);
if ((widget.doShowMe != oldWidget.doShowMe) || (widget.textContent != oldWidget.textContent)) {
if (widget.doShowMe) {
_doShowMe();
} else {
_doHideMe();
}
}
if (widget.doShowMe && widget.textContent != previousText) {
previousText = widget.textContent;
developer.log('reset');
_appearanceController
..reset()
..forward();
}
}
#override
void dispose() {
_appearanceController.dispose();
displayText = '';
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _appearanceController,
builder: (context, child) {
return Text(displayText);
});
}
void _doShowMe() {
_appearanceController
..duration = buildDuration
..forward();
}
void _doHideMe() {
_appearanceController
..duration = widget.hideDuration
..reverse();
}
}

Flutter stopwatch not running anymore

I am a beginner to flutter. I successfully created a stopwatch but once I updated flutter it stopped working entirely. It won't start or stop anymore. When running the code the stopwatch does not display the count up of numbers while still running fine behind the scenes. Does any know how to fix this, is my code now invalid, or do I need to add sometime new to make it work again? I will paste my code below, the first is the button to start the stopwatch and the second is my actual stopwatch code please let me know if you can help or need any more information.
child: GridView.count(
primary: false,
padding: const EdgeInsets.all(20),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
crossAxisCount: 3,
children: widget.tasks.map((element) {
final isActive = activeTask != null && activeTask == element;
return GestureDetector(
onTap: () {
// set active task or toggle is active
if (isActive) {
setState(() {
activeTask = null;
StudyViewModel.stopwatch.start();
disable = true;
});
} else {
setState(() {
activeTask = element;
StudyViewModel.stopwatch.stop();
disable = false;
});
}
},
child: Container(
color: isActive ? Colors.amber : Colors.green,
padding: EdgeInsets.all(8),
child: Center(
child: Text(
element.name,
style: TextStyle(color: Colors.white, fontSize: 25),
textAlign: TextAlign.center,
),
),
),
);
}).toList(),
study_viewmodel.dart
import 'dart:convert';
import 'dart:async' show Future;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:timestudyapp/models/elapsed_time.dart';
import 'package:timestudyapp/models/study.dart';
import 'package:path_provider/path_provider.dart';
class StudyViewModel {
static List<Study> studies = [];
static List<ValueChanged<ElapsedTime>> timerListeners =
<ValueChanged<ElapsedTime>>[];
static Stopwatch stopwatch = new Stopwatch();
/// load from file...
static Future load() async {
try {
File file = await getFile();
String studiesJson = await file.readAsString();
if (studiesJson.isNotEmpty) {
List studiesParsed = json.decode(studiesJson);
studies = studiesParsed.map((i) => Study.fromJson(i)).toList();
}
} catch (e) {
print(e);
}
}
static Future<File> getFile() async {
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
return File('$path/studies.json');
}
static Future saveFile() async {
File file = await getFile();
file.writeAsString(json.encode(studies));
}
static bool checkName(String name) {
bool match = false;
for (int i = 0; i < studies.length; i++) {
if (studies[i].name == name) {
match = true;
break;
}
}
return match;
}
static String milliToElapsedString(int milliseconds) {
final int hundreds = (milliseconds / 10).truncate();
final int seconds = (hundreds / 100).truncate();
final int minutes = (seconds / 60).truncate();
String hundredsStr = (hundreds % 100).toString().padLeft(2, '0');
String minutesStr = (minutes % 60).toString().padLeft(2, '0');
String secondsStr = (seconds % 60).toString().padLeft(2, '0');
return minutesStr + ':' + secondsStr + ':' + hundredsStr;
}
}
timer_text.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:timestudyapp/models/elapsed_time.dart';
import 'package:timestudyapp/viewmodels/study_viewmodel.dart';
import 'package:timestudyapp/widgets/hundreds.dart';
import 'package:timestudyapp/widgets/minutes_seconds.dart';
class TimerText extends StatefulWidget {
final double fontSize;
TimerText({required this.fontSize});
TimerTextState createState() => new TimerTextState();
}
class TimerTextState extends State<TimerText> {
late Timer timer;
late int milliseconds;
#override
void initState() {
timer = new Timer.periodic(Duration(milliseconds: 30), callback);
super.initState();
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
void callback(Timer timer) {
if (milliseconds != StudyViewModel.stopwatch.elapsedMilliseconds) {
milliseconds = StudyViewModel.stopwatch.elapsedMilliseconds;
final int hundreds = (milliseconds / 10).truncate();
final int seconds = (hundreds / 100).truncate();
final int minutes = (seconds / 60).truncate();
final ElapsedTime elapsedTime = new ElapsedTime(
hundreds: hundreds,
seconds: seconds,
minutes: minutes,
);
for (final listener in StudyViewModel.timerListeners) {
listener(elapsedTime);
}
}
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MinutesAndSeconds(fontSize: widget.fontSize),
Hundreds(fontSize: widget.fontSize),
],
);
}
}
timer_text.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:timestudyapp/models/elapsed_time.dart';
import 'package:timestudyapp/viewmodels/study_viewmodel.dart';
import 'package:timestudyapp/widgets/hundreds.dart';
import 'package:timestudyapp/widgets/minutes_seconds.dart';
class TimerText extends StatefulWidget {
final double fontSize;
TimerText({required this.fontSize});
TimerTextState createState() => new TimerTextState();
}
class TimerTextState extends State<TimerText> {
late Timer timer;
late int milliseconds;
#override
void initState() {
timer = new Timer.periodic(Duration(milliseconds: 30), callback);
super.initState();
}
#override
void dispose() {
timer.cancel();
super.dispose();
}
void callback(Timer timer) {
if (milliseconds != StudyViewModel.stopwatch.elapsedMilliseconds) {
milliseconds = StudyViewModel.stopwatch.elapsedMilliseconds;
final int hundreds = (milliseconds / 10).truncate();
final int seconds = (hundreds / 100).truncate();
final int minutes = (seconds / 60).truncate();
final ElapsedTime elapsedTime = new ElapsedTime(
hundreds: hundreds,
seconds: seconds,
minutes: minutes,
);
for (final listener in StudyViewModel.timerListeners) {
listener(elapsedTime);
}
}
}
#override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
MinutesAndSeconds(fontSize: widget.fontSize),
Hundreds(fontSize: widget.fontSize),
],
);
}
}
Maybe the problem is in the function onTap, you should change if (isActive) to if (!isActive)
GestureDetector(
onTap: () {
// set active task or toggle is active
if (!isActive) {
setState(() {
activeTask = null;
StudyViewModel.stopwatch.start();
disable = true;
});
} else {
setState(() {
activeTask = element;
StudyViewModel.stopwatch.stop();
disable = false;
});
}
},

Error : LateInitializationError: Field '_startDate#934496985' has not been initialized and Set State

I am building a calendar through syncfusion calendar on a flutter app and I have been getting an error that tells me "Field '' has not been initialized". I know that I need to initialize _startDate and _endDate but I am not sure what value it should be given.
Code :
class EventCalendar extends StatefulWidget {
const EventCalendar({Key? key}) : super(key: key);
#override
EventCalendarState createState() => EventCalendarState();
}
List<Color> _colorCollection = <Color>[];
List<String> _colorNames = <String>[];
int _selectedColorIndex = 0;
late DataSource _events;
Meeting? _selectedAppointment;
late DateTime _startDate;
late TimeOfDay _startTime;
late DateTime _endDate;
late TimeOfDay _endTime;
bool _isAllDay = false;
String _subject = '';
String _notes = '';
class EventCalendarState extends State<EventCalendar> {
EventCalendarState();
CalendarView _calendarView = CalendarView.month;
late List<String> eventNameCollection;
late List<Meeting> appointments;
#override
void initState() {
_calendarView = CalendarView.month;
appointments = getMeetingDetails();
_events = DataSource(appointments);
// initialize _startDate and _endDate here?
_selectedAppointment = null;
_selectedColorIndex = 0;
_subject = '';
_notes = '';
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
drawer: UserDrawer(),
appBar: AppBar(
iconTheme: IconThemeData(color: Colors.black),
backgroundColor: Colors.transparent,
elevation: 0,
centerTitle: true,
title: const Text('Itinerary',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.black)),
),
resizeToAvoidBottomInset: false,
body: Padding(
padding: const EdgeInsets.fromLTRB(5, 0, 5, 5),
child: getEventCalendar(_calendarView, _events, onCalendarTapped)),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add, color: Colors.white),
backgroundColor: Color(0xFF003893),
onPressed: () => Navigator.push<Widget>(
context,
MaterialPageRoute(
builder: (BuildContext context) => EventEditor()),
)));
}
SfCalendar getEventCalendar(
CalendarView _calendarView,
CalendarDataSource _calendarDataSource,
CalendarTapCallback calendarTapCallback) {
return SfCalendar(
view: _calendarView,
backgroundColor: Colors.transparent,
initialSelectedDate: DateTime.now(),
todayHighlightColor: Color(0xFF003893),
selectionDecoration: BoxDecoration(color: Colors.white60),
showNavigationArrow: true,
cellBorderColor: Colors.transparent,
firstDayOfWeek: 1,
onTap: calendarTapCallback,
allowedViews: [
CalendarView.day,
CalendarView.week,
CalendarView.month,
CalendarView.timelineWeek
],
monthViewSettings: MonthViewSettings(
showAgenda: true,
agendaViewHeight: 250,
appointmentDisplayMode: MonthAppointmentDisplayMode.appointment),
dataSource: _calendarDataSource,
initialDisplayDate: DateTime(DateTime.now().year, DateTime.now().month,
DateTime.now().day, 0, 0, 0),
timeSlotViewSettings: TimeSlotViewSettings(
minimumAppointmentDuration: const Duration(minutes: 60)),
);
}
void onCalendarViewChange(String value) {
if (value == 'Day') {
_calendarView = CalendarView.day;
} else if (value == 'Week') {
_calendarView = CalendarView.week;
} else if (value == 'Month') {
_calendarView = CalendarView.month;
} else if (value == 'Timeline week') {
_calendarView = CalendarView.timelineWeek;
}
setState(() {});
}
void onCalendarTapped(CalendarTapDetails calendarTapDetails) {
if (calendarTapDetails.targetElement != CalendarElement.appointment) {
return;
}
setState(() {
_selectedAppointment = null;
_isAllDay = false;
_selectedColorIndex = 0;
_subject = '';
_notes = '';
if (_calendarView == CalendarView.month) {
_calendarView = CalendarView.day;
} else {
if (calendarTapDetails.appointments != null &&
calendarTapDetails.appointments!.length == 1) {
final Meeting meetingDetails = calendarTapDetails.appointments![0];
_startDate = meetingDetails.from;
_endDate = meetingDetails.to;
_isAllDay = meetingDetails.isAllDay;
_selectedColorIndex =
_colorCollection.indexOf(meetingDetails.background);
_subject = meetingDetails.eventName == '(No title)'
? ''
: meetingDetails.eventName;
_notes = meetingDetails.description;
_selectedAppointment = meetingDetails;
} else {
final DateTime date = calendarTapDetails.date!;
_startDate = date;
_endDate = date.add(const Duration(hours: 1));
}
_startTime =
TimeOfDay(hour: _startDate.hour, minute: _startDate.minute);
_endTime = TimeOfDay(hour: _endDate.hour, minute: _endDate.minute);
Navigator.push<Widget>(
context,
MaterialPageRoute(builder: (BuildContext context) => EventEditor()),
);
}
});
}
List<Meeting> getMeetingDetails() {
final List<Meeting> meetingCollection = <Meeting>[];
eventNameCollection = <String>[];
eventNameCollection.add('');
_colorCollection = <Color>[];
_colorCollection.add(const Color(0xFF3D4FB5));
_colorCollection.add(const Color(0xFF0F8644));
_colorCollection.add(const Color(0xFF8B1FA9));
_colorCollection.add(const Color(0xFFD20100));
_colorCollection.add(const Color(0xFFFC571D));
_colorCollection.add(const Color(0xFF85461E));
_colorCollection.add(const Color(0xFFFF00FF));
_colorCollection.add(const Color(0xFFE47C73));
_colorCollection.add(const Color(0xFF636363));
_colorNames = <String>[];
_colorNames.add('Blue');
_colorNames.add('Green');
_colorNames.add('Purple');
_colorNames.add('Red');
_colorNames.add('Orange');
_colorNames.add('Caramel');
_colorNames.add('Magenta');
_colorNames.add('Peach');
_colorNames.add('Gray');
return meetingCollection;
}
}
class DataSource extends CalendarDataSource {
DataSource(List<Meeting> source) {
appointments = source;
}
#override
bool isAllDay(int index) => appointments![index].isAllDay;
#override
String getSubject(int index) => appointments![index].eventName;
#override
String getNotes(int index) => appointments![index].description;
#override
Color getColor(int index) => appointments![index].background;
#override
DateTime getStartTime(int index) => appointments![index].from;
#override
DateTime getEndTime(int index) => appointments![index].to;
}
class Meeting {
Meeting(
{required this.from,
required this.to,
this.background = Colors.green,
this.isAllDay = false,
this.eventName = '',
this.description = ''});
final String eventName;
final DateTime from;
final DateTime to;
final Color background;
final bool isAllDay;
final String description;
}
On top of the error, when I go to another page and return back to this calendar page, whatever events that was saved on it earlier on disappears already.
What value should I be giving to initialize _startDate and _endDate and how do I save the state of the page?
All state properties must be declared inside State class. late means that such variable will be initialized later (for example in initState method). If initialization time is not known make variable nullable, I.e. use question mark for type (e.g. DateTime?.
You don't initialize start and end dates, to be able to accept null use ?
like that
DateTime? _startDate;
DateTime? _endDate;
You can make them nullable, with DateTime? and before using them, where you got an error like when you want to access the hour property from them, you can check them if they are null, like this:
if([_startTime, _endTime].contains(null)) return;
Also, since you don't use the data outside of the handler and it is used after you change the value, you can initialize them with DateTime.now(), like this:
DateTime _startDate = DateTime.now();
And you can do this:
if (_calendarView == CalendarView.month) {
_calendarView = CalendarView.day;
return;
}
To remove the else that's coming after.
While specifying the variable as nullable we need to set the values before use them, in this case the AppointmentEditor class used the _startDate and _endDate values before initializing them, hence to use the values we must ensure that the values were set with values, hence before navigate to the editor page we must set values for the startDate and endDate variable with the required values, in this case you can use the selected date value from CalendarController or current date time value, or the date time value which you want to add the event in calendar.
In this shared code snippet, we have used current time value for the both variables as a default value, kindly set the _startDate and _endDate in the onPressed callback of the FloatingActionButton. Please find the attached code snippet for the same.
Code snippet:
onPressed: () {
_startDate??=DateTime.now();
_endDate??=DateTime.now();
Navigator.push<Widget>(
context,
MaterialPageRoute(
builder: (BuildContext context) => AppointmentEditor()),
)
}

Changing a variable doesn't change the view in flutter

I have an array of image location strings.
I have an int called displayImage in my class, and when I initialize an instance of that class, I set the value of displayImage as 0. So I see the image corresponding to the first image in the array.
Then I call a function, which, after a timeout, changes the value of the int from 0 to 1, but I don't see the corresponding second image. The image doesn't update. How to get the image to change when the index displayImage of the array changes?
var images = ["amusement1.jpeg", "amusement2.jpg", "amusement3.png", "amusement4.jpeg",
"amusement5.jpeg", "amusement6.jpeg"];
class RandomWords extends StatefulWidget {
#override
_RandomWordsState createState() => _RandomWordsState(0);
}
class _RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = TextStyle(fontSize: 10.0);
int displayImage;
_RandomWordsState(firstImage) {
this.displayImage = firstImage;
}
Widget _buildRow(WordPair pair) {
return ListTile(
title: Text(
pair.asPascalCase,
style: _biggerFont,
)
);
}
final timeout = Duration(seconds: 3);
void handleTimeout() {
print(this.displayImage);
this.displayImage++;
print(this.displayImage);
}
switchImages () {
print("switching image");
const timeout = Duration(seconds: 3);
const ms = Duration(milliseconds: 1);
Timer startTimeout(milliseconds) {
var duration = milliseconds == null ? timeout : ms * milliseconds;
return Timer(duration, handleTimeout);
}
startTimeout(1000);
}
Widget _buildSuggestions() {
return ListView.builder(
padding: EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _suggestions.length) {
_suggestions.addAll(generateWordPairs().take(10));
}
return _buildRow(_suggestions[index]);
});
}
#override
Widget build(BuildContext context) {
switchImages();
String imageUrl = "assets/amusement/" + images[displayImage];
final wordPair = WordPair.random();
return Image(image: AssetImage(imageUrl));
}
}
Try updating this:
void handleTimeout() {
print(this.displayImage);
this.displayImage++;
print(this.displayImage);
}
To this:
void handleTimeout() {
print(this.displayImage);
setState((){
this.displayImage++;
});
print(this.displayImage);
}
To make the Widget rebuild, you should trigger the change using the setState method.

Flutter- detect memory leak

I'm little bit confused because I was thinking there are no memory leak in flutter since there is no concept of weak (if I'm correct).
I'm running this on iOS device.
I'm trying to play videos and initialize some videos beforehand so that user can see it without delay.
To do that I prepared six VideoPlayerController and make those always being initialized while current video is playing.
There are three more initialized VideoPlayerController next to current one and two more initialized ones before current one like image below.
With this logic I play video very smoothly back and forth. But after play about ten videos, app crush because of memory issue.
I tried every function Future, async, await but still eats lots of memories.
I'm not sure but it might be NotificationListener?
Since onNotification returns bool not Future or
is this something to do with main thread or something?
Does anyone know how to fix this memory issue?
Code:
class _SwiperScreenState extends State<SwiperScreen> {
VideoPlayerController _firstController;
VideoPlayerController _secondController;
VideoPlayerController _thirdController;
VideoPlayerController _fourthController;
VideoPlayerController _fifthController;
VideoPlayerController _sixthController;
List<VideoPlayerController> _controllers;
List<String> urls = [
'https://firebasestorage.googleapis.com/v0/b/waitingboy-34497.appspot.com/o/video%2F8-21%2F1534825377992OsfJfKsdf90K8sf?alt=media&token=12245ee4-1598-4f7e-ba28-a9eb72ca474e',
'http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4',
'https://firebasestorage.googleapis.com/v0/b/waitingboy-34497.appspot.com/o/video%2F8-21%2F1534825377992OsfJfKsdf90K8sf?alt=media&token=12245ee4-1598-4f7e-ba28-a9eb72ca474e',
'http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4',
];
int currentIndex = 0; //refer to current playing controller index
int videosIndex = 0; //refer to current playing urls index
bool _didGetNotification(ScrollNotification notification) {
if (notification is UserScrollNotification) {
if (notification.direction.toString() == 'ScrollDirection.reverse') {
//swipe to left so add one more video
videosIndex++;
//modify index so that always in the range of 0 ~ 5.
if (currentIndex <= 2) {
final int prepareIndex = currentIndex + 3;
urls.add(
'https://firebasestorage.googleapis.com/v0/b/waitingboy-34497.appspot.com/o/video%2F8-21%2F1534825377992OsfJfKsdf90K8sf?alt=media&token=12245ee4-1598-4f7e-ba28-a9eb72ca474e');
_initVideo(urls[videosIndex], prepareIndex);
} else {
final int prepareIndex = (currentIndex + 3) - 6;
urls.add(
'http://www.sample-videos.com/video/mp4/720/big_buck_bunny_720p_20mb.mp4');
_initVideo(urls[videosIndex], prepareIndex);
}
}
if (notification.direction.toString() == 'ScrollDirection.forward') {
//swipe to right so back one more video
videosIndex--;
//modify index so that always in the range of 0 ~ 5 .
if (currentIndex >= 2) {
final int videoIndex = videosIndex - 2;
final int prepareIndex = currentIndex - 2;
_initVideo(urls[videoIndex], prepareIndex);
} else {
final int videoIndex = videosIndex - 2;
final int prepareIndex = 4 + currentIndex;
_initVideo(urls[videoIndex], prepareIndex);
}
}
}
return true;
}
Future _initVideo(String url, int initIndex) async {
if (_controllers[initIndex] != null) {
await _controllers[initIndex].dispose();
}
_controllers[initIndex] = new VideoPlayerController.network(url);
await _controllers[initIndex].initialize().then((_) async => await _controllers[initIndex].setLooping(true));
setState(() {});
}
Future _initFirstThree() async {
for (int i = 1; i < urls.length; i++) {
await _initVideo(urls[i], i);
}
}
#override
void initState() {
_controllers = [
_firstController,
_secondController,
_thirdController,
_fourthController,
_fifthController,
_sixthController
];
_initVideo(urls[0], 0).then((_) => _controllers[0].play());
_initFirstThree();
super.initState();
}
#override
void deactivate() {
_controllers[currentIndex].setVolume(0.0);
_controllers[currentIndex].pause();
super.deactivate();
}
#override
void dispose() {
_controllers.forEach((con) {
con.dispose();
});
super.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Swiper'),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.disc_full),
onPressed: () {
Navigator
.of(context)
.push(MaterialPageRoute(builder: (context) => Dissmiss()));
},
)
],
),
body: new NotificationListener(
onNotification: _didGetNotification,
child: new Swiper(
itemCount: 6,
itemBuilder: (BuildContext context, int index) {
return _controllers[index].value.initialized
? new AspectRatio(
aspectRatio: _controllers[index].value.aspectRatio,
child: new VideoPlayer(_controllers[index]),
)
: new Center(child: new CircularProgressIndicator());
},
loop: urls.length > 6 ? true : false,
onIndexChanged: (i) async {
currentIndex = i;
final int pauseIndex = i == 0 ? 5 : i - 1;
await _controllers[pauseIndex].pause().then((_) async {
await _controllers[i].play();
});
},
),
),
);
}
}