Flutter stopwatch not running anymore - flutter

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;
});
}
},

Related

Flutter Shared Preference : How to save the Stateful widget to the device (lines are drawn by CustomPainter and stylus)

I'm making a e-book application and user can draw lines on the WorkbookDrawingPage Widget by Stack layer.
I want to make the drawn lines saved to the device and books by using shared preference. I tried to use a Json encoding, but couldn't apply to my code.
How to save List<Object> to SharedPreferences in Flutter?
Here's my code
WorkbookViewPage ( PdfViewer and DrawingPage widget)
class WorkbookViewPage extends StatefulWidget {
String downloadedURL;
String workbookName;
WorkbookViewPage(this.downloadedURL, this.workbookName, {super.key});
#override
State<WorkbookViewPage> createState() => _WorkbookViewPageState();
}
class _WorkbookViewPageState extends State<WorkbookViewPage> {
/// Widget Key
GlobalKey _viewKey = GlobalKey();
final GlobalKey<SfPdfViewerState> _pdfViewerKey = GlobalKey();
/// Controller
PdfViewerController _pdfViewerController = PdfViewerController();
ScrollController _scrollController = ScrollController();
/// Variables
int _lastClosedPage = 1;
int _currentPage = 1;
bool _memoMode = false;
int drawingPage = 1;
bool isPenTouched = false;
/// pen size control widget Entry
bool isOverlayVisible = false;
OverlayEntry? entry;
/// Hide Overlay Widget
void hideOverlay() {
entry?.remove();
entry = null;
isOverlayVisible = false;
}
/// Count the Total Pages of the Workbook
int _countTotalPages(){
int _totalPages = _pdfViewerController.pageCount;
return _totalPages;
}
/// get Last closed page of workbook
void _loadLastClosedPage() async {
final pagePrefs = await SharedPreferences.getInstance();
_lastClosedPage = pagePrefs.getInt('${widget.workbookName}') ?? 1;
}
#override
void initState() {
super.initState();
// Load Pdf with landScape Mode
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft]);
_pdfViewerController = PdfViewerController();
String workbookName = '${widget.workbookName}';
_loadLastClosedPage();
_countTotalPages();
print("======== Loaded workbook name = ${workbookName} ==========");
}
#override
Widget build(BuildContext context) {
/// Drawing Provider
var p = context.read<DrawingProvider>();
/// set _lastClosedPage
void countLastClosedPage() async {
final pagePrefs = await SharedPreferences.getInstance();
setState(() {
pagePrefs.setInt('${widget.workbookName}', _lastClosedPage);
_lastClosedPage = (pagePrefs.getInt('${widget.workbookName}') ?? 1);
});
}
return Scaffold(
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.white,
elevation: 1,
),
body: Stack(
children: [
InteractiveViewer(
panEnabled: _memoMode ? false : true,
scaleEnabled: _memoMode ? false : true,
maxScale: 3,
child: Stack(
children: [
IgnorePointer(
ignoring: true,
child: SfPdfViewer.network(
widget.downloadedURL,
controller: _pdfViewerController,
key: _pdfViewerKey,
pageLayoutMode: PdfPageLayoutMode.single,
enableDoubleTapZooming: false,
// Save the last closed page number
onPageChanged: (details) {
_lastClosedPage = details.newPageNumber;
_currentPage = details.newPageNumber;
countLastClosedPage();
},
onDocumentLoaded: (details) {
_pdfViewerController.jumpToPage(_lastClosedPage);
_pdfViewerController.zoomLevel = 0;
print("totalpages = ${_countTotalPages().toInt()}");
},
canShowScrollHead: false,
),
),
SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
controller: _scrollController,
scrollDirection: Axis.horizontal,
child: WorkbookDrawingPage(widget.workbookName, _countTotalPages().toInt(),),
),
],
),
),
WorkbookDawingPage.dart
class WorkbookDrawingPage extends StatefulWidget {
String workbookName;
int countTotalPages;
WorkbookDrawingPage(this.workbookName, this.countTotalPages,{super.key});
#override
State<WorkbookDrawingPage> createState() => _WorkbookDrawingPageState();
}
class _WorkbookDrawingPageState extends State<WorkbookDrawingPage> {
// OverlayEntry widget key
GlobalKey _overlayViewKey = GlobalKey();
Key _viewKey = UniqueKey();
// pen size control widget Entry
bool isOverlayVisible = false;
OverlayEntry? entry;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
var p = context.read<DrawingProvider>();
void hideOverlay() {
entry?.remove();
entry = null;
isOverlayVisible = false;
}
return Container(
width: MediaQuery.of(context).size.width * (widget.countTotalPages),
height: MediaQuery.of(context).size.height * 1,
child: CustomPaint(
painter: DrawingPainter(p.lines),
child: Listener(
behavior: HitTestBehavior.translucent,
// Draw lines when stylus hit the screen
onPointerDown: (s) async {
if (s.kind == PointerDeviceKind.stylus) {
setState(() {
p.penMode ? p.penDrawStart(s.localPosition) : null;
p.highlighterMode
? p.highlighterDrawStart(s.localPosition)
: null;
p.eraseMode ? p.erase(s.localPosition) : null;
});
}
/// Stylus with button pressed touched the Screen
else if (s.kind == PointerDeviceKind.stylus ||
s.buttons == kPrimaryStylusButton) {
setState(() {
p.changeEraseModeButtonClicked = true;
});
}
},
onPointerMove: (s) {
if (s.kind == PointerDeviceKind.stylus) {
setState(() {
p.penMode ? p.penDrawing(s.localPosition) : null;
p.highlighterMode
? p.highlighterDrawing(s.localPosition)
: null;
p.eraseMode ? p.erase(s.localPosition) : null;
});
} else if (s.kind == PointerDeviceKind.stylus ||
s.buttons == kPrimaryStylusButton) {
setState(() {
p.changeEraseModeButtonClicked = true;
});
}
},
),
),
);
}
}
The application uses provider for drawing lines and here's provider class
Drawing Provider
Class DrawingProvider extends ChangeNotifier {
/// line List **/
final lines = <List<DotInfo>>[];
Color _selectedColor = Colors.black;
Color get selectedColor => _selectedColor;
/** function method to change Color **/
set changeColor(Color color) {
_selectedColor = color;
notifyListeners();
}
/** Mode Selection **/
bool _penMode = false;
bool get penMode => _penMode;
bool _highlighterMode = false;
bool get highlighterMode => _highlighterMode;
bool _eraseMode = false;
bool get eraseMode => _eraseMode;
bool _memoMode = false;
bool get memoMode => _memoMode;
set changeEraseModeButtonClicked(bool eraseMode) {
eraseMode = true;
_eraseMode = eraseMode;
print("eraseMode가 On ");
notifyListeners();
}
/** 지우개 선택 모드 **/
void changeEraseMode() {
_eraseMode = !_eraseMode;
print("eraseMode is : ${eraseMode}");
_penMode = false;
_highlighterMode = false;
notifyListeners();
}
/** 일반 펜 선택 모드 **/
void changePenMode() {
_penMode = !_penMode;
// _selectedColor = _selectedColor.withOpacity(1);
print("penMode is called : ${penMode} ");
_eraseMode = false;
_highlighterMode = false;
notifyListeners();
}
/** 형광펜 선택 모드 **/
void changeHighlighterMode() {
_highlighterMode = !_highlighterMode;
// _selectedColor = _selectedColor.withOpacity(0.3);
print("Highlighter Mode : ${highlighterMode}");
_eraseMode = false;
_penMode = false;
_opacity = 0.3;
notifyListeners();
}
/** Pen Draw Start **/
void penDrawStart(Offset offset) async {
var oneLine = <DotInfo>[];
oneLine.add(DotInfo(offset, penSize, _selectedColor,));
lines.add(oneLine);
notifyListeners();
}
/** Pen Drawing **/
void penDrawing(Offset offset) {
lines.last.add(DotInfo(offset, penSize, _selectedColor));
notifyListeners();
}
/** highlighter Start **/
void highlighterDrawStart(Offset offset) {
var oneLine = <DotInfo>[];
oneLine
.add(DotInfo(offset, highlighterSize, _selectedColor.withOpacity(0.3),));
lines.add(oneLine);
notifyListeners();
}
/** 터치 후 형광 펜 그리기 **/
void highlighterDrawing(Offset offset) {
lines.last
.add(DotInfo(offset, highlighterSize, _selectedColor.withOpacity(0.3)));
notifyListeners();
}
/** 선 지우기 메서드 **/
void erase(Offset offset) {
final eraseRange = 15;
for (var oneLine in List<List<DotInfo>>.from(lines)) {
for (var oneDot in oneLine) {
if (sqrt(pow((offset.dx - oneDot.offset.dx), 2) +
pow((offset.dy - oneDot.offset.dy), 2)) <
eraseRange) {
lines.remove(oneLine);
break;
}
}
}
notifyListeners();
}
}
and the Dotinfo save the information of the lines
DotInfo.dart
class DotInfo {
final Offset offset;
final double size;
final Color color;
DotInfo(this.offset, this.size, this.color);
}
in the WorkbookDrawingPage.dart , CustomPaint.painter method get DrawingPainter Class
DrawingPainter.dart
class DrawingPainter extends CustomPainter {
final List<List<DotInfo>> lines;
DrawingPainter(this.lines);
#override
void paint(Canvas canvas, Size size) {
for (var oneLine in lines) {
Color? color;
double? size;
var path = Path();
var l = <Offset>[];
for (var oneDot in oneLine) {
color ??= oneDot.color;
size ??= oneDot.size;
l.add(oneDot.offset);
}
path.addPolygon(l, false);
canvas.drawPath(
path,
Paint()
..color = color!
..strokeWidth = size!
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..isAntiAlias = true
..strokeJoin = StrokeJoin.round);
}
}
#override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
The Drawing function is related to the provider.
I have no idea how to save the drawn lines into the device. My idea was saving the WorkbookDrawingPage widget itself to the device by using shared preference but, shared preference only can save Int, String, double... so, I tried to encode the DotInfo Class but it cannot be encoded because constructors value is not initialized in the DotInfo Class.
how to save the drawn lines to the device and make them pair with the book?
is saving the widget to the device is possible?? or can you give me any hint or answer?

How to play list of video URLs forever or until stopped in flutter? I am stuck :(

I want to create a video player that continuously plays videos until stopped. Thanks to "easeccy" currently I am able to play videos one after another with a buffer, but it stops after playing the last video. I have been trying to loop all these videos...please help :(
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoPlayerDemo extends StatefulWidget {
const VideoPlayerDemo({Key? key}) : super(key: key);
#override
State<VideoPlayerDemo> createState() => _VideoPlayerDemoState();
}
class _VideoPlayerDemoState extends State<VideoPlayerDemo> {
int index = 0;
double _position = 0;
double _buffer = 0;
bool lock = true;
final Map<String, VideoPlayerController> _controllers = {};
final Map<int, VoidCallback> _listeners = {};
final Set<String> _urls = {
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4#1',
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4#2'
};
#override
void initState() {
super.initState();
_playListLoop();
if (_urls.isNotEmpty) {
_initController(0).then((_) {
_playController(0);
});
}
if (_urls.length > 1) {
_initController(1).whenComplete(() => lock = false);
}
}
VoidCallback _listenerSpawner(index) {
return () {
int dur = _controller(index).value.duration.inMilliseconds;
int pos = _controller(index).value.position.inMilliseconds;
int buf = _controller(index).value.buffered.last.end.inMilliseconds;
setState(() {
if (dur <= pos) {
_position = 0;
return;
}
_position = pos / dur;
_buffer = buf / dur;
});
if (dur - pos < 1) {
if (index < _urls.length - 1) {
_nextVideo();
}
}
};
}
VideoPlayerController _controller(int index) {
return _controllers[_urls.elementAt(index)]!;
}
Future<void> _initController(int index) async {
var controller = VideoPlayerController.network(_urls.elementAt(index));
_controllers[_urls.elementAt(index)] = controller;
await controller.initialize();
}
void _removeController(int index) {
_controller(index).dispose();
_controllers.remove(_urls.elementAt(index));
_listeners.remove(index);
}
void _stopController(int index) {
_controller(index).removeListener(_listeners[index]!);
_controller(index).pause();
_controller(index).seekTo(const Duration(milliseconds: 0));
}
void _playController(int index) async {
if (!_listeners.keys.contains(index)) {
_listeners[index] = _listenerSpawner(index);
}
_controller(index).addListener(_listeners[index]!);
await _controller(index).play();
setState(() {});
}
void _previousVideo() {
if (lock || index == 0) {
return;
}
lock = true;
_stopController(index);
if (index + 1 < _urls.length) {
_removeController(index + 1);
}
_playController(--index);
if (index == 0) {
lock = false;
} else {
_initController(index - 1).whenComplete(() => lock = false);
}
}
void _nextVideo() async {
if (lock || index == _urls.length - 1) {
return;
}
lock = true;
_stopController(index);
if (index - 1 >= 0) {
_removeController(index - 1);
}
_playController(++index);
if (index == _urls.length - 1) {
lock = false;
} else {
_initController(index + 1).whenComplete(() => lock = false);
}
}
_playListLoop() async {
if (index + 1 == _urls.length) {
if (_controller(index).value.position ==
_controller(index).value.position) {
setState(() {
_controller(index).initialize();
});
}
}
}
#override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.topCenter,
children: <Widget>[
SizedBox.expand(
child: FittedBox(
fit: BoxFit.cover,
child: SizedBox(
width: _controller(index).value.size.width,
height: _controller(index).value.size.height,
child: VideoPlayer(
_controller(index)..setVolume(0.0),
),
),
),
),
//Container ends here.
],
);
}
}
Tried while loop and for loop but its repeating the last loaded video only...

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();
}
}

Countdown timer date and time format in 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(),
],
),
),
),
),
);
}
}

How to loop the audio to keep playing until 3 times-Flutter

I am currently using audioplayers: ^0.20.1 to play and resume the video, right now I would like to loop the audio 3 times (keep looping to play the audio). As I know audioplayers package has loop property but I still don't know how to custom the loop property
Here is how you can loop the audio player:
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: AudioPlayerLoopTesting()));
}
class AudioPlayerLoopTesting extends StatefulWidget {
#override
_AudioPlayerLoopTestingState createState() => _AudioPlayerLoopTestingState();
}
class _AudioPlayerLoopTestingState extends State<AudioPlayerLoopTesting> {
AudioCache audioCache = AudioCache();
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
void _playLoopAudio() {
int timesPlayed = 0;
const timestoPlay = 3;
//audio.mp3 is the local asset file
audioCache.play('audio.mp3').then((player) {
player.onPlayerCompletion.listen((event) {
timesPlayed++;
if (timesPlayed >= timestoPlay) {
timesPlayed = 0;
player.stop();
} else {
player.resume();
}
});
});
}
Widget localAsset() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Loop Local Asset'),
ElevatedButton(
child: const Text('Loop the audio'),
onPressed: _playLoopAudio,
),
],
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: localAsset(),
),
);
}
}
Thumbs up if problem resolved
to loop the audio try this :-
static AudioCache musicCache;
static AudioPlayer instance;
void playLoopedMusic() async {
musicCache = AudioCache(prefix: "audio/");
instance = await musicCache.loop("bgmusic.mp3");
// await instance.setVolume(0.5); you can even set the volume
}
void pauseMusic() {
if (instance != null) {
instance.pause(); }}
to loop it 3 time only :-
int numberOftimesToPlay=0;
playThreeTimes(){
_audioPlayer = AudioPlayer();
int res = await _audioPlayer.play("https://192.168.1.66/$sUrl");
//await _audioPlayer.play("http://192.168.1.5/00001.mp3");
if (res == 1 & numberOftimesToPlay>4) {
numberOftimesToPlay ++;
playThreeTimes()
print("ok");
} else {
print("done");
}}