Persistent Ticker in Flutter - flutter

How to get a persistent tick at every frame refresh time. For example in Flame game engine update method gets called at around every 1/60 seconds and a value dt with elapsed time is passed.
I want to implement one simple animation where a fan will rotate. I want to change its rotation speed depending on user input. My idea is that at every tick I will rotate the fan image/ container at a fixed value. As the user increases the speed I will increase the multiplier. There are few options like using the Flame engine or Flare, but they seem overkill. Also, I can use SingleTickerProviderMixin but there are few overheads like reverse the animation when finished and forwarded it and so...
I think there will be a simple solution, which will notify me at each frame refresh time that occurs at around every 1/60 seconds, and pass me the elapsed time dt (around 167 mS or so).

A nice way to do it (without Animation widgets), is to implement a Timer with a Stream; see the example below:
import 'package:flutter/material.dart';
import "dart:async";
const frequency = Duration(milliseconds: 50);
void main() => runApp(
MaterialApp(
home: Material(
child: Center(
child: Container(
color: Colors.white,
child: MyWidget(),
),
),
),
),
);
class MyWidget extends StatefulWidget {
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
final StreamController<double> _streamer =
StreamController<double>.broadcast();
Timer timer;
double _rotation = 0.0;
#override
void initState() {
super.initState();
timer = Timer.periodic(frequency, (t) {
_rotation++;
_streamer.add(1);
});
}
#override
Widget build(BuildContext context) {
return StreamBuilder<double>(
initialData: 0,
stream: _streamer.stream,
builder: (context, snapshot) {
return Transform(
transform: Matrix4.rotationZ(_rotation),
child: Text('Hello, World!'),
);
});
}
}

I would also make sure to implement the dispose() callback if you copy this code. You need to make sure to cancel() any running timers to prevent odd behaviors or they will become a source of memory leaks.
The timer = null; is not always needed, but there are situations where the state object will hold a reference to the timer var itself and also cause a memory leak. For example, if you capture the timer var inside the timer callback body.
Example:
#override
void dispose() {
timer?.cancel();
timer = null;
super.dispose();
}

Related

How to make a timer constantly display

I have made a chess clock but the time only updates when you click one of the buttons and I want it so the time is constantly updating. I have the setState() function after the button press but no ware else. How could this be mad to updating every 10th of a second or so. Thanks. Here is my code:
import 'package:flutter/material.dart';
...
Widget build(context) {
return MaterialApp(
home: Scaffold(// this is what creates the page, everything should be inside this cuz everything is inside the page
appBar: AppBar(//what is displayed at the top WOW
title: Text('chess clock'),
),
body: Column(
children: [
Container(
margin: EdgeInsets.all(150.0),
child: RaisedButton(
onPressed: () {
setState((){
if (button1==1){
button1=2;
minus=minus2-minus;
fminus=fminus+minus;
print(minus);
divid=DateTime.now().microsecondsSinceEpoch/1000000-start/1000000;
divid.round();
divid=divid-fminus;
minus=DateTime.now().microsecondsSinceEpoch/1000000;
divid=60.0-divid;
}
});
},
child: Text(divid.round().toString()),
)
),
Container(
margin: EdgeInsets.all(150.0),
child: RaisedButton(
onPressed: () {
setState((){
if (button1==2){
button1=1;
minus2=minus-minus2;
fminus2=fminus2+minus2;
divid2=DateTime.now().microsecondsSinceEpoch/1000000-start/1000000;
divid2.round();
divid2=divid2-fminus2;
minus2=DateTime.now().microsecondsSinceEpoch/1000000;
divid2=60.0-divid2;
}
});
},
child: Text(divid2.round().toString()),
...
For a complete solution, you should put Kauli's code in your initState() method and tear it down in dispose(). It would look something like this.
I've made some small changes to fit the needs of the solution.
Edit: I added the surrounding StatefulWidget class and removed some cut and paste which was confusing. You can wrap the build method with your original Scaffold and other widgets. I left that out to make the answer a little clearer.
class ChessClock extends StatefulWidget {
#override
_ChessClockState createState() => _ChessClockState();
}
class _ChessClockState extends State<ChessClock> {
// Important to capture the timer object reference
Timer chessClockTimer;
Timer periodicSec() {
// How could this be made to updating every 10th of a second or so.
// Per op question
return Timer.periodic(Duration(milliseconds: 10), (_){
setState((){
if (button1==2){
button1=1;
minus2=minus-minus2;
fminus2=fminus2+minus2;
divid2=DateTime.now().microsecondsSinceEpoch/1000000-start/1000000;
divid2.round();
divid2=divid2-fminus2;
minus2=DateTime.now().microsecondsSinceEpoch/1000000;
divid2=60.0-divid2;
}
});
});
}
#override
void initState() {
super.initState();
chessClockTimer = periodicSec(); // Kauli's code
}
#override
void dispose() {
// Stop the timer
chessClockTimer?.cancel();
// Clean up the timer
chessClockTimer?.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// Your build method code
}
}
You can use the Timer.periodic to do this
void periodicSec() {
Timer.periodic(Duration(seconds: 1), (_){
setState((){
if (button1==2){
button1=1;
minus2=minus-minus2;
fminus2=fminus2+minus2;
divid2=DateTime.now().microsecondsSinceEpoch/1000000-start/1000000;
divid2.round();
divid2=divid2-fminus2;
minus2=DateTime.now().microsecondsSinceEpoch/1000000;
divid2=60.0-divid2;
}
});
});
}

How to get StatefulWidget's state?

I am new to flutter and the way I get StatefulWidget's state is add a state property to widget, eg:
// ignore: must_be_immutable
class _CustomContainer extends StatefulWidget {
_CustomContainer({Key key}) : super(key: key);
#override
__CustomContainerState createState() {
state = __CustomContainerState();
return state;
}
__CustomContainerState state;
void changeColor() {
if (state == null) return;
// call state's function
this.state.changeColor();
}
}
class __CustomContainerState extends State<_CustomContainer> {
var bgColor = Colors.red;
#override
Widget build(BuildContext context) {
return Container(
width: 200,
height: 200,
color: bgColor,
);
}
void changeColor() {
setState(() {
bgColor = Colors.blue;
});
}
}
usage:
final redContainer = _CustomContainer();
final button = FlatButton(
onPressed: () {
// call widget function
redContainer.changeColor();
},
child: Text('change color'),
);
It works, but I wonder is there any hidden danger?
You'll notice it's very awkward to manipulate Flutter widgets in an imperative fashion, like in the question. This is because of the declarative approach Flutter has taken to building screens.
Declarative vs. Imperative
The approach / philosophy of Flutter UI is a declarative UI vs. an imperative UI.
The example in the question above leans toward an imperative approach.
create an object
object holds state (information)
object exposes method
use method to impose change on object → UI changes
A declarative approach:
there is state (information) above your object
your object is declared (created) from that state
if the state changes...
your object is recreated with the changed state
Below I've tried to convert the imperative approach above, into a declarative one.
CustomContainer is declared with a color; state known / kept outsideCustomContainer & used in its construction.
After construction, you cannot impose a color change on CustomContainer. In an imperative framework you would expose a method, changeColor(color) and call that method and the framework would do magic to show a new color.
In Flutter, to change color of CustomContainer, you declare CustomContainer with a new color.
import 'package:flutter/material.dart';
/// UI object holds no modifiable state.
/// It configures itself once based on a declared color.
/// If color needs to change, pass a new color for rebuild
class CustomContainer extends StatelessWidget {
final Color color;
CustomContainer(this.color);
#override
Widget build(BuildContext context) {
return Container(
color: color,
child: Text('this is a colored Container'),
);
}
}
/// A StatefulWidget / screen to hold state above your UI object
class DeclarativePage extends StatefulWidget {
#override
_DeclarativePageState createState() => _DeclarativePageState();
}
class _DeclarativePageState extends State<DeclarativePage> {
var blue = Colors.blueAccent.withOpacity(.3);
var red = Colors.redAccent.withOpacity(.3);
Color color;
// state (e.g. color) is held in a context above your UI object
#override
void initState() {
super.initState();
color = blue;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Declarative Page'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomContainer(color),
// color "state" ↑ is passed in to build/rebuild your UI object
RaisedButton(
child: Text('Swap Color'),
onPressed: () {
setState(() {
toggleColor();
});
}
)
],
),
),
);
}
void toggleColor() {
color = color == blue ? red : blue;
}
}
Read more on declarative vs imperative on Flutter.dev.
setState() Rebuilds & Performance
Performance-wise it seems wasteful to rebuild the entire screen when a single widget, way down in the widget tree needs rebuilding.
When possible (and sensible) it's better to wrap the particular elements that have state & need rebuilding in a StatefulWidget, rather than wrapping your entire page in a StatefulWidget and rebuilding everything. (Likely, this wouldn't even be a problem, I'll discuss further below.)
Below I've modified the above example, moving the StatefulWidget from being the entire DeclarativePage, to a ChangeWrapper widget.
ChangeWrapper will wrap the CustomContainer (which changes color).
DeclarativePage is now a StatelessWidget and won't be rebuilt when toggling color of CustomContainer.
import 'package:flutter/material.dart';
class ChangeWrapper extends StatefulWidget {
#override
_ChangeWrapperState createState() => _ChangeWrapperState();
}
class _ChangeWrapperState extends State<ChangeWrapper> {
final blue = Colors.blueAccent.withOpacity(.3);
final red = Colors.redAccent.withOpacity(.3);
Color _color; // this is state that changes
#override
void initState() {
super.initState();
_color = blue;
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomContainer(_color),
RaisedButton(
child: Text('Swap Color'),
onPressed: () {
setState(() {
toggleColor();
});
}
)
],
);
}
void toggleColor() {
_color = _color == blue ? red : blue;
}
}
/// UI object holds no state, it configures itself once based on input (color).
/// If color needs to change, pass a new color for rebuild
class CustomContainer extends StatelessWidget {
final Color color;
CustomContainer(this.color);
#override
Widget build(BuildContext context) {
return Container(
color: color,
child: Text('this is a colored Container'),
);
}
}
class DeclarativePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
print('Declarative Page re/built');
return Scaffold(
appBar: AppBar(
title: Text('Declarative Page'),
),
body: Center(
child: ChangeWrapper(),
),
);
}
}
When running this version of the code, only the ChangeWrapper widget is rebuilt when swapping colors via button press. You can watch the console output for "Declarative Page re/built" which is written to debug console only once upon first screen build/view.
If DeclarativePage was huge with hundreds of widgets, isolating widget rebuilds in the above manner could be significant or useful. On small screens like in the first example at top or even or average screens with a couple dozen widgets, the difference in savings are likely negligible.
Flutter was designed to operate at 60 frames per second. If your screen can build / rebuild all widgets within 16 milliseconds (1000 milliseconds / 60 frames = 16.67 ms per frame), the user will not see any jankiness.
When you use animations, those are designed to run at 60 frames (ticks) per second. i.e. the widgets in your animation will be rebuilt 60 times each second the animation runs.
This is normal Flutter operation for which it was designed & built. So when you're considering whether your widget architecture could be optimized it's useful to think about its context or how that group of widgets will be used. If the widget group isn't in an animation, built once per screen render or once per human button tap... optimization is likely not a big concern.
A large group of widgets within an animation... likely a good candidate to consider optimization & performance.
Btw, this video series is a good overview of the Flutter architecture. I'm guessing Flutter has a bunch of hidden optimizations such as Element re-use when a Widget hasn't materially/substantially changed, in order to save on CPU cycles instantiating, rendering & positioning Element objects.
add setState() method where you want to add state

Redraw CustomPaint without updating state / rebuilding widget?

I'm trying to understand how customPaint works, I want to draw a custom frame by frame animation on a canvas.
I can make it work by redrawing the widget every 1/60 seconds, but that doesn't sound very efficient. I would like render the CustomPainter every 1/60 seconds but that doesn't seem to work. Any note or remark very appreciated to help me understand how I'm supposed to achieve this. Thanks.
This is the kind of code I'm working with :
class CustomAnimatedWidgetState extends State<CustomAnimatedWidget> {
CustomPaint _paint=null;
MyCustomPainter _painter=null;
double animationFrame=0;
void tick() {
//called eery 1/60 seconds
animationFrame+=1/60;
_painter.setAnimationFrame(animationFrame);
_paint.METHOD_I_DONT_KNOW_TO_FORCE_REDRAW();
// I want to avoid setState({animationFrame+=1/60;}); which works actually, but that doesn't sound very efficient to redraw the widget every 1/60 seconds, unless it's the right way to do it ?
}
#override
Widget build(BuildContext context) {
//developer.log('axis='+axis.toString(), name: 'DEBUG');
_painter=MyCustomPainter();
_painter.setAnimationFrame(animationFrame);
_paint=CustomPaint(
painter: _painter,
child: Container(),
);
return _paint;
}
}
Thx to #pskink for the hint in the comments, here is the working solution, using a ChangeNotifier when calling the constructor of the MyCustomPainter class.
class CustomAnimatedWidgetState extends State<CustomAnimatedWidget> {
CustomPaint _paint=null;
MyCustomPainter _painter=null;
ChangeNotifier _repaint=ChangeNotifier();
double animationFrame=0;
void tick() {
//called eery 1/60 seconds
animationFrame+=1/60;
_painter.setAnimationFrame(animationFrame);
_repaint.notifyListeners();
}
#override
Widget build(BuildContext context) {
_painter=MyCustomPainter(repaint:_repaint);
_painter.setAnimationFrame(animationFrame);
_paint=CustomPaint(
painter: _painter,
child: Container(),
);
return _paint;
}
}

How Can I PAUSE or RESUME a async task using a button in flutter?

I'm Building An Flutter Application which requires image changes after a period of time. I thought using while loop with a sleep method inside may solve the problem. But It didn't, Image is only getting change after the loop ends. Application UI also gets froze.
So, I used the async Task which I can't control with a Button.
Desired Output: Image should be changed after every 10 seconds and the user can pause or resume method execution.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Test(
),
),
)
);
}}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
int imgnumber=1;
int varToCheckButtonPress = 0;
String BtnTxt = "START";
void inc(){
while(imgnumber<10)
{
print(imgnumber);
await Future.delayed(const Duration(seconds: 10));
setState(() {
imgnumber++;
});
}
}
#override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(flex: 1,
child: Container(
child: Image.asset('images/'+imgnumber.toString()+'.png'),
height: 500,
width:500,
color: Colors.green,
),
),
FlatButton(
child: Text(BtnTxt),
onPressed: (){
if (varToCheckButtonPress == 0) {
setState(() {
inc();
BtnTxt = 'PAUSE';
varToCheckButtonPress = 1;
});
} else if (varToCheckButtonPress == 1) {
setState(() {
BtnTxt = 'RESUME';
varToCheckButtonPress = 0;
});
}
},
)
],
);
}
}
I want the user to control the UI with a single button behave as START, PAUSE and RESUME.
Can we Use normal function To implement this functionality?
You should make use of Bloc pattern to manage your states, e.g: StreamBuilder, Providers, and make a timer to push new imageUrl to the sink and let the streamBuilder receive the latest imageUrl.
As for your button, all it controls is the timer. When u hit the play button, new imageUrl will keep pushing to the sink, while you press paused, simply stop the timer, and new image Url will not be pushing new imageUrl to the sink, and of course, reset the timer when you hit the stop button.
Here is a very detail Bloc pattern tutorial you can follow: Medium
The shortcut to achieve this is :
You can probably hold a function in async loop and call setState method on tap to change it's state.
For example :
call this function in desired location
while (_isPaused) {
await Future.delayed(Duration(milliseconds: 500));
}
and then call set state method from onTap, just like this
onTap:(){
setState((){
_isPaused? _isPaused=false: _isPaused=true;
});
}

Raw touch/swipe data in Flutter? Compared to Java w/ Android Studio?

I am very new to flutter development, and I have to make a fairly quick decision on whether or not it is the right platform for my internship project.
I have to create an interface which requires all directional swipes to navigate to different menus (I'm thinking of doing nested horizontal and vertical scrolling, which I have had trouble with in Android Studio) - but more importantly, I have to save the raw data from the touching/tapping/swiping. I can't just save "left swipe" or "right swipe", I also have to know pressure, velocity, location, exact path, etc.
Is this feasible in flutter? How does flutter handle this raw data as opposed to Android studio? Does flutter only determine the approximate direction of the swipe and that's it?
I have tried searching for answers all day, but I must be missing some key word, because I have been unable to find the answer so far.
GestureDetector is a very extensive Widget in this regard. It has all the capabilities you are searching for. A simpler version of it, which also has Material design built in, is InkWell, but this might be lacking some of the functionality you are searching for.
With a GestureDetector wrapped about your Widget you will be able to catch all hit events (you can even specify HitTestBehavior (with the behavior parameter).
For your custom interactions there are plenty of callbacks implemented. I linked you to the constructor, which contains a bunch of useful parameters, like onTapDown/Up, onVertical/HorizontalDragStart/Update/End.
This is not even everything, but using those you can programatically define your behavior. Let me explain the concept with a small example:
Offset start;
void verticalDragStart(DragStartDetails details) {
// process the start by working with the details
start = details.globalPosition;
// ...
}
void verticalDragUpdate(DragUpdateDetails details) {
// apply your logic
Offset delta = details.delta;
// ...
}
// use DragEnd, also for horizontal, pan etc.
#override
Widget build(BuildContext context) => GestureDectector(
onVerticalDragStart: verticalDragStart,
// ...
);
I hope that you can imagine all the possibilties this enables. You can also combine different callbacks. Just take a look at the parameters in the documentation and experiment with what fits for you.
I think that this is the raw data you asked for.
You can get the raw touch movements using Listener. The following example shows how to grab a list of points. It uses them to draw the line you just traced with your finger. You can't tell the pressure, but can tell the exact path and velocity (if you stored time with each point). The higher level detector, GestureDetector, hides these raw movements from you, but interprets them into the traditional swipes.
(Notes about the example... shouldRepaint should be smarter, points returned by Listener are in global co-ordinates so may need to be converted to local (this simple example works because there's no AppBar))
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Gesture',
home: new MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Offset> points = [];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Listener(
onPointerDown: (e) {
points = [];
points.add(e.position);
},
onPointerMove: (e) {
points.add(e.position);
},
onPointerUp: (e) {
points.add(e.position);
setState(() {});
},
child: new CustomPaint(
painter: new PathPainter(points),
child: new Container(
width: 300.0,
height: 300.0,
color: Colors.black12,
),
),
),
);
}
}
class PathPainter extends CustomPainter {
List<Offset> points;
Path path = new Path();
PathPainter(this.points) {
if (points.isEmpty) return;
Offset origin = points[0];
path.moveTo(origin.dx, origin.dy);
for (Offset o in points) {
path.lineTo(o.dx, o.dy);
}
}
#override
void paint(Canvas canvas, Size size) {
canvas.drawPath(
path,
new Paint()
..color = Colors.orange
..style = PaintingStyle.stroke
..strokeWidth = 4.0,
);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true; // todo - determine if the path has changed
}
}