I'm trying to clone the timer of the app I use when I workout: Strong. So far, I've managed to implement all the main features and animations (which aren't many). I know my code is messy and there's probably better ways to implement what I'm trying to do if there are any suggestions on that they're also appreciated. I'm having two problems with my application. 1. When the timer starts I get the following error:
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════
flutter: The following NoSuchMethodError was thrown building Peinture(dirty, state: _PeintureState#82e34):
flutter: The getter 'inMilliseconds' was called on null.
flutter: Receiver: null
flutter: Tried calling: inMilliseconds
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:50:5)
flutter: #1 _PeintureState.build (package:workout_timer/main.dart:241:72)
flutter: #2 StatefulElement.build (package:flutter/src/widgets/framework.dart:3809:27)
flutter: #3 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3721:15)
flutter: #4 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #5 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #6 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #7 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4867:14)
flutter: #8 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #9 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #10 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #11 ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #12 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #13 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #14 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #15 StatelessElement.update (package:flutter/src/widgets/framework.dart:3781:5)
flutter: #16 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #17 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4585:32)
flutter: #18 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4975:17)
flutter: #19 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #20 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #21 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #22 ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #23 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #24 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #25 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #26 ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #27 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #28 RenderObjectElement.updateChildren (package:flutter/src/widgets/framework.dart:4585:32)
flutter: #29 MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4975:17)
flutter: #30 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #31 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #32 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #33 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #34 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #35 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #36 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #37 ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #38 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #39 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #40 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #41 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #42 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #43 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4867:14)
flutter: #44 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #45 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #46 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #47 StatelessElement.update (package:flutter/src/widgets/framework.dart:3781:5)
flutter: #48 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #49 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:4867:14)
flutter: #50 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #51 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #52 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #53 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #54 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #55 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #56 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #57 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #58 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #59 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #60 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #61 ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #62 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #63 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #64 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #65 ProxyElement.update (package:flutter/src/widgets/framework.dart:3990:5)
flutter: #66 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #67 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #68 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #69 StatefulElement.update (package:flutter/src/widgets/framework.dart:3878:5)
flutter: #70 Element.updateChild (package:flutter/src/widgets/framework.dart:2742:15)
flutter: #71 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:3732:16)
flutter: #72 Element.rebuild (package:flutter/src/widgets/framework.dart:3547:5)
flutter: #73 BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2286:33)
flutter: #74 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:676:20)
flutter: #75 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:219:5)
flutter: #76 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:990:15)
flutter: #77 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:930:9)
flutter: #78 _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:842:5)
flutter: #79 _invoke (dart:ui/hooks.dart:154:13)
flutter: #80 _drawFrame (dart:ui/hooks.dart:143:3)
flutter: ════════════════════════════════════════════════════════════════════════════════════════════════════
Which I've made sure to check I'm not getting anything null but I still get it.
And the second.
When the timer finishes I get the following error:
[VERBOSE-2:shell.cc(184)] Dart Error: Unhandled exception:
setState() called after dispose(): _TimeState#10749(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#0 State.setState.<anonymous closure> (package:flutter/src/widgets/framewo<…>
And when I try to start a timer again I get this:
flutter: Another exception was thrown: NoSuchMethodError: The getter 'inMilliseconds' was called on null.
Here's is my code:
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fluttery/layout.dart';
Duration time;
Duration previousTime;
Duration currentTime;
AnimationController timeController, circleController;
bool reverse = false;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
Stopwatch stopwatch = new Stopwatch();
#override
void initState() {
super.initState();
timeController = new AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
);
timeController.addListener(() {
setState(() {});
});
}
#override
void dispose() {
timeController.dispose();
circleController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Center(
child: Opacity(
opacity: timeController.value != null ? 1.0 - timeController.value : 1.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 1);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("1:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 2);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("2:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 3);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("3:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 5);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("5:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
],
),
),
),
Center(
child: Opacity(
opacity: timeController.value != null ? 0.0 + timeController.value : 0.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: time != null ? buildTimes(stopwatch, time, previousTime) : []
),
),
),
CenterAbout(
position: Offset(MediaQuery.of(context).size.width / 2,
MediaQuery.of(context).size.height / 2),
child: Peinture(color: Color.fromRGBO(224, 245, 255, 1.0), animate: false)
),
CenterAbout(
position: Offset(MediaQuery.of(context).size.width / 2,
MediaQuery.of(context).size.height / 2),
child: Peinture(color: Colors.blue, animate: true)
)
],
),
);
}
}
List<Widget> buildTimes(stopwatch, time, previousTime) {
return [
Time(stopwatch: stopwatch, time: time),
Text(
previousTime != null
? previousTime.toString().substring(3, 7)
: "",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30.0,
color: Colors.black54),
),
];
}
class Peinture extends StatefulWidget {
final Color color;
final bool animate;
Peinture({this.color, this.animate});
#override
_PeintureState createState() => _PeintureState(color: color, animate: animate);
}
class _PeintureState extends State<Peinture> {
Timer timer;
Color color;
bool animate;
_PeintureState({this.color, this.animate}) {
timer = Timer.periodic(Duration(milliseconds: 1), callback);
}
void callback(Timer timer) {
setState(() {
if (currentTime != null) {
print(currentTime.inMilliseconds);
if (currentTime.inMilliseconds <= 0) {
reverse = true;
timeController.reverse();
previousTime = null;
time = previousTime;
currentTime = time;
reverse = false;
}
}
});
}
#override
Widget build(BuildContext context) {
return Opacity(
opacity: reverse == false ? 1.0 : timeController.value,
child: CustomPaint(
painter: CirclePainter(
radius: 150.0,
thickness: 8.0,
color: color,
startAngle: -pi / 2,
endAngle: time != null && animate ? -pi / 2 + (((currentTime.inMilliseconds - circleController.value)) / 60000) * (2*pi) : -pi / 2 + (2 * pi) - (0 / (2*pi)),
),
),
);
}
}
class CirclePainter extends CustomPainter {
final double radius;
final double thickness;
final Color color;
final double startAngle;
final double endAngle;
final Paint circlePaint;
CirclePainter({this.radius, this.thickness, this.color, this.startAngle, this.endAngle})
: circlePaint = new Paint()
..color = color
..strokeWidth = thickness
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
#override
void paint(Canvas canvas, Size size) {
canvas.drawArc(Rect.fromLTWH(-radius, -radius, radius * 2, radius * 2),
startAngle, endAngle - startAngle, false, circlePaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class Time extends StatefulWidget {
final Duration time;
final Stopwatch stopwatch;
Time({this.stopwatch, this.time});
#override
_TimeState createState() => _TimeState(stopwatch: stopwatch, time: time);
}
class _TimeState extends State<Time> {
Timer timer;
Duration time;
Stopwatch stopwatch;
_TimeState({this.time, this.stopwatch}) {
timer = new Timer.periodic(new Duration(milliseconds: 1), callback);
}
void callback(Timer timer) {
if (stopwatch.isRunning) {
setState(() {
currentTime = time - stopwatch.elapsed;
});
}
}
#override
Widget build(BuildContext context) {
return Text(
currentTime != null ? currentTime.toString().substring(3, 7) : "",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 60.0, color: Colors.black87),
);
}
}
Thanks for the help and again any suggestions as to how you'd go about it would be appreciated.
Try this
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:fluttery/layout.dart';
Duration time;
Duration previousTime;
Duration currentTime;
AnimationController timeController, circleController;
bool reverse = false;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
Stopwatch stopwatch = new Stopwatch();
#override
void initState() {
super.initState();
timeController = new AnimationController(
duration: const Duration(milliseconds: 250),
vsync: this,
);
timeController.addListener(() {
setState(() {});
});
}
#override
void dispose() {
timeController.dispose();
circleController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Center(
child: Opacity(
opacity: timeController.value != null
? 1.0 - timeController.value
: 1.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 1);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("1:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 2);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("2:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 3);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("3:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
new FlatButton(
onPressed: () {
setState(() {
previousTime = Duration(minutes: 5);
time = previousTime;
timeController.forward();
stopwatch.start();
circleController = new AnimationController(
duration: Duration(milliseconds: time.inMilliseconds),
vsync: this,
);
circleController.addListener(() {
setState(() {});
});
circleController.forward();
});
},
child: Text("5:00",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 16.0)),
),
],
),
),
),
Center(
child: Opacity(
opacity: timeController.value != null
? 0.0 + timeController.value
: 0.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: time != null
? buildTimes(stopwatch, time, previousTime)
: []),
),
),
CenterAbout(
position: Offset(MediaQuery.of(context).size.width / 2,
MediaQuery.of(context).size.height / 2),
child: Peinture(
color: Color.fromRGBO(224, 245, 255, 1.0), animate: false),
),
CenterAbout(
position: Offset(MediaQuery.of(context).size.width / 2,
MediaQuery.of(context).size.height / 2),
child: Peinture(color: Colors.blue, animate: true),
)
],
),
);
}
}
List<Widget> buildTimes(stopwatch, time, previousTime) {
return [
Time(stopwatch: stopwatch, time: time),
Text(
previousTime != null ? previousTime.toString().substring(3, 7) : "",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 30.0, color: Colors.black54),
),
];
}
class Peinture extends StatefulWidget {
final Color color;
final bool animate;
Peinture({this.color, this.animate});
#override
_PeintureState createState() =>
_PeintureState(color: color, animate: animate);
}
class _PeintureState extends State<Peinture> {
Timer timer;
Color color;
bool animate;
_PeintureState({this.color, this.animate}) {
timer = Timer.periodic(Duration(milliseconds: 1), callback);
}
void callback(Timer timer) {
setState(() {
if (currentTime != null) {
if (currentTime.inMilliseconds <= 0) {
reverse = true;
timeController.reverse();
previousTime = null;
time = previousTime;
currentTime = time;
reverse = false;
}
}
});
}
#override
Widget build(BuildContext context) {
return Opacity(
opacity: reverse == false ? 1.0 : timeController.value,
child: CustomPaint(
painter: CirclePainter(
radius: 150.0,
thickness: 8.0,
color: color,
startAngle: -pi / 2,
endAngle: time != null && animate && currentTime != null
? -pi / 2 +
(((currentTime.inMilliseconds - circleController.value)) /
60000) *
(2 * pi)
: -pi / 2 + (2 * pi) - (0 / (2 * pi)),
),
),
);
}
}
class CirclePainter extends CustomPainter {
final double radius;
final double thickness;
final Color color;
final double startAngle;
final double endAngle;
final Paint circlePaint;
CirclePainter(
{this.radius, this.thickness, this.color, this.startAngle, this.endAngle})
: circlePaint = new Paint()
..color = color
..strokeWidth = thickness
..style = PaintingStyle.stroke
..strokeCap = StrokeCap.round;
#override
void paint(Canvas canvas, Size size) {
canvas.drawArc(Rect.fromLTWH(-radius, -radius, radius * 2, radius * 2),
startAngle, endAngle - startAngle, false, circlePaint);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class Time extends StatefulWidget {
final Duration time;
final Stopwatch stopwatch;
Time({this.stopwatch, this.time});
#override
_TimeState createState() => _TimeState(stopwatch: stopwatch, time: time);
}
class _TimeState extends State<Time> {
Timer timer;
Duration time;
Stopwatch stopwatch;
_TimeState({this.time, this.stopwatch}) {
timer = new Timer.periodic(new Duration(milliseconds: 1), callback);
}
void callback(Timer timer) {
if (stopwatch.isRunning) {
setState(() {
currentTime = time - stopwatch.elapsed;
});
}
}
#override
Widget build(BuildContext context) {
return Text(
currentTime != null ? currentTime.toString().substring(3, 7) : "",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 60.0, color: Colors.black87),
);
}
}
Related
I'm currently developing an app for deaf people and I need to use many gifs inside my app.
I don't want my gifs to play more than once so I used GifImage which lets me control the number of reproductions and the speed of the gif locally.
Now I realize that with so many gifs, my app will be too heavy to upload to the stores, so I tried using CachedNetworkImage to play my gifs from firebase storage. The problem with this library is that I have no control on the reproduction of the gifs, they play infinitely.
What I have already tried:
Play gif from local file:
GifImage(
controller: controller,
image: AssetImage('test.gif'),
)
Play gif using CachedNetworkImage:
CachedNetworkImage(
imageUrl:
'https://firebasestorage.googleapis.com/v0/b/app-lsc-7310d.appspot.com/o/test.gif?alt=media',
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width * 0.5,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
I want to know if it is possible to use both libraries together so I can store the gifs in the cache and also have control over them, like so:
CachedNetworkImage(
imageUrl:
'https://firebasestorage.googleapis.com/v0/b/app-lsc-7310d.appspot.com/o/test.gif?alt=media',
imageBuilder: (context, imageProvider) => GifImage(
controller: controller,
image: imageProvider,
),
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
But I get the following error:
[VERBOSE-2:shell.cc(242)] Dart Unhandled Exception: NoSuchMethodError: The getter 'buffer' was called on null.
Receiver: null
Tried calling: buffer, stack trace: #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 fetchGif (package:flutter_gifimage/flutter_gifimage.dart:243:76)
#2 GifImageState.didChangeDependencies (package:flutter_gifimage/flutter_gifimage.dart:158:7)
#3 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:4653:11)
#4 ComponentElement.mount (package:flutter/src/widgets/framework.dart:4469:5)
#5 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3541:14)
#6 Element.updateChild (package:flutter/src/widgets/framework.dart:3303:20)
#7 SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:5981:14)
#8 Element.updateChild (package:flutter/src/widgets/framework.dart:3293:15)
#9 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:4520:16)
#10<…>
Here is the full code in case you want to try it out:
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
GifController controller;
String currentGif = 'test';
double frameNum = 25.0;
void initState() {
controller = GifController(vsync: this);
super.initState();
}
playGif(String gifToPlay) async {
controller.value = 0;
controller.animateTo(frameNum - 1,
duration: Duration(milliseconds: (160 * (frameNum - 1)).toInt()));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Giphy'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CachedNetworkImage(
imageUrl:
'https://firebasestorage.googleapis.com/v0/b/app-lsc-7310d.appspot.com/o/test.gif?alt=media',
fit: BoxFit.cover,
width: MediaQuery.of(context).size.width * 0.5,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
SizedBox(
height: 20.0,
),
Card(
child: GifImage(
controller: controller,
image: AssetImage('images/test.gif'),
),
elevation: 7,
),
SizedBox(
height: 20.0,
),
GestureDetector(
onTap: () {
playGif(currentGif);
},
child: Container(
color: Colors.blueGrey,
padding: EdgeInsets.all(5.0),
child: Text(
'Play Local Gif',
style: TextStyle(fontSize: 20),
),
),
),
],
),
),
);
}
}
I am open to other suggestions as well and would be really grateful for your help.
I have a flutter app, which streams video from an ESP32 Camera. The code I am interested in should take a snapshot of the image. However, when I press the button, I get the error below.
I have tried changing the resolution in the code and also the resolution on the camera sending the stream. Both gave me the same error.
Can anyone help me, please?
E/flutter (22722): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: Exception: Invalid image dimensions.
E/flutter (22722): #0 Scene.toImage (dart:ui/compositing.dart:28:7)
E/flutter (22722): #1 OffsetLayer.toImage (package:flutter/src/rendering/layer.dart:1208:26)
E/flutter (22722): #2 RenderRepaintBoundary.toImage (package:flutter/src/rendering/proxy_box.dart:2960:24)
E/flutter (22722): #3 _HomeState.takeScreenShot (package:traincam/main.dart:173:32)
E/flutter (22722): #4 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:993:19)
E/flutter (22722): #5 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1111:38)
E/flutter (22722): #6 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:183:24)
E/flutter (22722): #7 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:598:11)
E/flutter (22722): #8 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:287:5)
E/flutter (22722): #9 BaseTapGestureRecognizer.handlePrimaryPointer (package:flutter/src/gestures/tap.dart:222:7)
E/flutter (22722): #10 PrimaryPointerGestureRecognizer.handleEvent (package:flutter/src/gestures/recognizer.dart:476:9)
E/flutter (22722): #11 PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:77:12)
E/flutter (22722): #12 PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:122:9)
E/flutter (22722): #13 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
E/flutter (22722): #14 PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:120:18)
E/flutter (22722): #15 PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:106:7)
E/flutter (22722): #16 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:358:19)
E/flutter (22722): #17 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:338:22)
E/flutter (22722): #18 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:267:11)
E/flutter (22722): #19 GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:295:7)
E/flutter (22722): #20 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:240:7)
E/flutter (22722): #21 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:213:7)
E/flutter (22722): #22 _rootRunUnary (dart:async/zone.dart:1206:13)
E/flutter (22722): #23 _CustomZone.runUnary (dart:async/zone.dart:1100:19)
E/flutter (22722): #24 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1005:7)
E/flutter (22722): #25 _invoke1 (dart:ui/hooks.dart:265:10)
E/flutter (22722): #26 _dispatchPointerDataPacket (dart:ui/hooks.dart:174:5)
Here is the code:
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:gesture_zoom_box/gesture_zoom_box.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:save_in_gallery/save_in_gallery.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
title: "Train Cam",
home: Home(
channel: IOWebSocketChannel.connect('ws://192.168.1.213:8888'),
)
);
}
}
class Home extends StatefulWidget {
final WebSocketChannel channel;
Home({Key key, #required this.channel}) : super (key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
final videoWidth = 640;
final videoHeight = 480;
double newVideoSizeWidth = 640;
double newVideoSizeHeight = 480;
bool isLandscape = false;
String _timeString;
var _globalKey = new GlobalKey();
final _imageSaver = ImageSaver();
#override
void initState() {
super.initState();
isLandscape = false;
}
#override
void dispose() {
widget.channel.sink.close();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: OrientationBuilder(
builder: (context, orientation) {
var screenWidth = MediaQuery.of(context).size.width;
var screenHeight = MediaQuery.of(context).size.height;
if (orientation == Orientation.portrait) {
//screenWidth < screenHeight
isLandscape = false;
newVideoSizeWidth =
screenWidth > videoWidth ? videoWidth : screenWidth;
newVideoSizeHeight =
videoHeight * newVideoSizeHeight / screenWidth;
}
else {
isLandscape = true;
newVideoSizeHeight =
screenHeight > videoHeight ? videoHeight : screenHeight;
newVideoSizeWidth =
videoWidth * newVideoSizeHeight / videoHeight;
}
return Container(
color: Colors.black,
child: StreamBuilder(
stream: widget.channel.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
);
} else {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
SizedBox(
height: isLandscape ? 0 : 30,
),
Stack(
children: <Widget>[
RepaintBoundary(
key: _globalKey,
),
GestureZoomBox(
maxScale: 5.0,
doubleTapScale: 2.0,
duration: Duration(milliseconds: 200),
child: Image.memory(
snapshot.data,
gaplessPlayback: true,
width: newVideoSizeWidth,
height: newVideoSizeWidth,
),
),
Positioned.fill(
child: Align(
child: Column(
children: <Widget>[
SizedBox(
height: 16,
),
Text('Train Cam', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w300),),
SizedBox(
height: 8,
),
Text('Live | $_timeString', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w300),),
]
),
alignment: Alignment.topCenter,
))
],
),
Expanded(flex: 1,
child: Container(
color: Colors.black,
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
IconButton(icon: Icon(Icons.videocam, size: 24,), onPressed: () { },),
IconButton(icon: Icon(Icons.photo_camera, size: 24,), onPressed: takeScreenShot,),
IconButton(icon: Icon(Icons.mic, size: 24,), onPressed: () { },),
IconButton(icon: Icon(Icons.speaker, size: 24,), onPressed: () { },),
IconButton(icon: Icon(Icons.add_alert, size: 24,), onPressed: () { },)
],
),
)
),)
],
),
],
);
}
},
),
);
}
)
);
}
takeScreenShot()async{
RenderRepaintBoundary boundary = _globalKey.currentContext.findRenderObject();
var image = await boundary.toImage();
var byteData = await image.toByteData(format: ImageByteFormat.png);
var pngBytes = byteData.buffer.asUint8List();
final res = await _imageSaver.saveImage(imageBytes: pngBytes);
Fluttertoast.showToast(msg: res? "Screenshot saved" : "Screenshot failed!",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0);
}
}
Invalid Image Dimensions in Flutter when taking a video snapshot,
Which means you need to set constraints for the error widget
For example:
I have the same problem with the following code:
Widget carIconImage =
Image.asset(BYDConstants().getBydImageHome("box.png"));
Wrapped with Container with width and height, them problem solve.
Widget carIconImage =
Container(width:30,height:30,child: Image.asset(BYDConstants().getBydImageHome("box.png")),);
I wonder how to animate a container widget to leave the screen to the left.
How do I do that?
Thanks!
This is a way you can do it with SlideTransition
class SlideContainerToTheLeft extends StatefulWidget {
#override
_SlideContainerToTheLeftState createState() =>
_SlideContainerToTheLeftState();
}
class _SlideContainerToTheLeftState extends State<SlideContainerToTheLeft>
with SingleTickerProviderStateMixin {
var tween = Tween<Offset>(begin: Offset.zero, end: Offset(-2, 0))
.chain(CurveTween(curve: Curves.ease));
AnimationController animationController;
#override
void initState() {
// TODO: implement initState
super.initState();
animationController =
AnimationController(vsync: this, duration: Duration(seconds: 2));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SlideTransition(
position: animationController.drive(tween),
child: Container(
width: 300,
height: 400,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20), color: Colors.blue),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
animationController.forward();
},
),
);
}
}
Hope this helps you.
I have a page containing a page view slider with a page controller and I get this error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception:
Looking up a deactivated widget's ancestor is unsafe. E/flutter (
7013): At this point the state of the widget's element tree is no
longer stable. E/flutter ( 7013): To safely refer to a widget's
ancestor in its dispose() method, save a reference to the ancestor by
calling dependOnInheritedWidgetOfExactType() in the widget's
didChangeDependencies() method.
I'm trying to solve it but I can't and I don't know the reason for this error.
This my code which I use:
var pageController = PageController(initialPage: 0);
var pageViewModelData = List<PageViewData>();
Timer sliderTimer;
var currentShowIndex = 0;
#override
void initState() {
pageViewModelData.add(PageViewData(
titleText: 'Cape Town',
subText: 'Extraordinary five-star\noutdoor activites',
assetsImage: 'assets/images/explore_2.jpg',
));
pageViewModelData.add(PageViewData(
titleText: 'Find best deals',
subText: 'Extraordinary five-star\noutdoor activites',
assetsImage: 'assets/images/explore_1.jpg',
));
pageViewModelData.add(PageViewData(
titleText: 'Find best deals',
subText: 'Extraordinary five-star\noutdoor activites',
assetsImage: 'assets/images/explore_3.jpg',
));
sliderTimer = Timer.periodic(Duration(seconds: 4), (timer) {
if (currentShowIndex == 0) {
pageController.animateTo(MediaQuery.of(context).size.width, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
} else if (currentShowIndex == 1) {
pageController.animateTo(MediaQuery.of(context).size.width * 2, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
} else if (currentShowIndex == 2) {
pageController.animateTo(0, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
});
super.initState();
}
#override
void dispose() {
sliderTimer?.cancel();
super.dispose();
}
I get the error start from this line:
MediaQuery.of(context).size.width
Can anyone tell me the reason for this issue! Thanks
You can copy paste run full code below
Value of MediaQuery.of(context).size.width is not not ready in initState()
You can use WidgetsBinding.instance.addPostFrameCallback
code snippet
WidgetsBinding.instance.addPostFrameCallback((_) {
print('width ${MediaQuery.of(context).size.width}');
sliderTimer = Timer.periodic(Duration(seconds: 4), (timer) {
if (currentShowIndex == 0) {
pageController.animateTo(MediaQuery.of(context).size.width, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
} else if (currentShowIndex == 1) {
pageController.animateTo(MediaQuery.of(context).size.width * 2, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
} else if (currentShowIndex == 2) {
pageController.animateTo(0, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
});
});
working demo
output
I/flutter ( 1876): width 411.42857142857144
full code
import 'dart:io';
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(new MaterialApp(
title: 'Flutter Tutorial',
home: new AnimatedIconExample(),
));
}
class AnimatedIconExample extends StatefulWidget {
#override
_AnimatedIconExampleState createState() => _AnimatedIconExampleState();
}
class _AnimatedIconExampleState extends State<AnimatedIconExample>
with SingleTickerProviderStateMixin {
PageController pageController = PageController();
AnimationController controller;
Timer sliderTimer;
var currentShowIndex = 0;
#override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: Duration(seconds: 1));
WidgetsBinding.instance.addPostFrameCallback((_) {
print('width ${MediaQuery.of(context).size.width}');
sliderTimer = Timer.periodic(Duration(seconds: 4), (timer) {
if (currentShowIndex == 0) {
pageController.animateTo(MediaQuery.of(context).size.width, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
} else if (currentShowIndex == 1) {
pageController.animateTo(MediaQuery.of(context).size.width * 2, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
} else if (currentShowIndex == 2) {
pageController.animateTo(0, duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
});
});
}
#override
void dispose() {
controller?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: GestureDetector(
child: Center(
child: AnimatedIcon(
icon: AnimatedIcons.menu_close, progress: controller),
),
onTap: () {
controller.reverse();
pageController.animateToPage(0,
duration: Duration(seconds: 1), curve: Curves.linear);
},
),
title: Text("Animated PageView Controller")),
body: PageView(
controller: pageController,
scrollDirection: Axis.vertical,
children: <Widget>[
buildPage0(),
Container(color: Colors.green),
Container(color: Colors.blue),
Container(color: Colors.yellow),
Container(color: Colors.pink),
],
),
);
}
show(int page) {
controller.forward();
pageController.animateToPage(page,
duration: Duration(seconds: 1), curve: Curves.linear);
}
Container buildPage0() {
return Container(
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListTile(onTap: () => show(1), title: Center(child: Text("Green"))),
ListTile(onTap: () => show(2), title: Center(child: Text("Blue"))),
ListTile(onTap: () => show(3), title: Center(child: Text("Yellow"))),
ListTile(onTap: () => show(4), title: Center(child: Text("Pink"))),
],
),
);
}
}
Issue
I made an ellipse loading animation inside of flutter, but had to use a Timer on all three different controllers. (See example below...)
Is there any widget that can help chaining three different animations?
I tried using Interval Widget for multiple curves, but it did not provide a smooth transition. e.g. Interval(0.0, 0.3), Interval(0.3, 0.6), Interval(0.6, 0.9) for the animation curves.
Sample
Sample Code
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
import 'dart:async';
void main() {
runApp(
new MaterialApp(
home: new Scaffold(
body: new CircleLoader(),
)
)
);
}
class CircleLoader extends StatefulWidget {
#override
_CircleLoaderState createState() => new _CircleLoaderState();
}
class _CircleLoaderState extends State<CircleLoader>
with TickerProviderStateMixin {
Animation<double> animation;
Animation<double> animation2;
Animation<double> animation3;
AnimationController controller;
AnimationController controller2;
AnimationController controller3;
int duration = 1000;
Widget circle = new Container(
height: 10.0,
width: 10.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[300],
),
);
#override
initState() {
super.initState();
controller = new AnimationController(
duration: new Duration(milliseconds: duration), vsync: this);
controller2 = new AnimationController(
duration: new Duration(milliseconds: duration), vsync: this);
controller3 = new AnimationController(
duration: new Duration(milliseconds: duration), vsync: this);
final CurvedAnimation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeInOut);
final CurvedAnimation curve2 =
new CurvedAnimation(parent: controller2, curve: Curves.easeInOut);
final CurvedAnimation curve3 =
new CurvedAnimation(parent: controller3, curve: Curves.easeInOut);
animation = new Tween(begin: 0.85, end: 1.5).animate(curve)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
animation2 = new Tween(begin: 0.85, end: 1.5).animate(curve2)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller2.reverse();
} else if (status == AnimationStatus.dismissed) {
controller2.forward();
}
});
animation3 = new Tween(begin: 0.85, end: 1.5).animate(curve3)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller3.reverse();
} else if (status == AnimationStatus.dismissed) {
controller3.forward();
}
});
controller.forward();
new Timer(const Duration(milliseconds: 300), () {
controller2.forward();
});
new Timer(const Duration(milliseconds: 600), () {
controller3.forward();
});
}
#override
Widget build(BuildContext context) {
return new Center(
child: new Container(
width: 100.0,
height: 50.0,
color: Colors.grey,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new ScaleTransition(scale: animation, child: circle),
new ScaleTransition(scale: animation2, child: circle),
new ScaleTransition(scale: animation3, child: circle),
],
),
),
);
}
}
The trick is that you can create your own tween.
In short, what you want is a smooth curve that goes from 0 to 1 and then 1 to 0 smoothly. Which you can assimilate to a (sin(t * 2 * PI) + 1) / 2 where 0 <= t <= 1
And then delay that curve for each cicles.
class TestTween extends Tween<double> {
final double delay;
TestTween({double begin, double end, this.delay}) : super(begin: begin, end: end);
#override
double lerp(double t) {
return super.lerp((sin((t - delay) * 2 * PI) + 1) / 2);
}
}
Which allows to do
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
instead of having to add an animation listener and reverse the animation.
The end result is
class CircleLoader extends StatefulWidget {
#override
_CircleLoaderState createState() => new _CircleLoaderState();
}
class _CircleLoaderState extends State<CircleLoader>
with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
}
#override
dispose() {
_controller.dispose();
super.dispose();
}
buildCircle(double delay) {
return new ScaleTransition(
scale: new TestTween(begin: .85, end: 1.5, delay: delay)
.animate(_controller),
child: new Container(
height: 10.0,
width: 10.0,
decoration: new BoxDecoration(
shape: BoxShape.circle,
color: Colors.grey[300],
),
),
);
}
#override
Widget build(BuildContext context) {
return new Center(
child: new Container(
width: 100.0,
height: 50.0,
color: Colors.grey,
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
buildCircle(.0),
buildCircle(.2),
buildCircle(.4),
],
),
),
);
}
}
class TestTween extends Tween<double> {
final double delay;
TestTween({double begin, double end, this.delay})
: super(begin: begin, end: end);
#override
double lerp(double t) {
return super.lerp((sin((t - delay) * 2 * PI) + 1) / 2);
}
}