Reputation: 5944
I have flutter widget. It is SingleChildScrollView with children + "middle cursor":
I can scroll this list left and right - the cursor always remains in the middle. Now I need to scroll programmatically the "children" relative to the cursor with a frequency of one second.
I tried adding a timer and a thread and processing this in my widget. Also I added SteamBuilder in my widget, but I get an error in one frame.
My error frame:
My normal frame:
This error occurs after one frame. Any ideas how to fix this - u will be very grateful.
This is my full code:
void main() => //runApp(MyApp());
runApp(StartApp());
class StartApp extends StatefulWidget {
@override
_StartAppState createState() => _StartAppState();
}
class _StartAppState extends State<StartApp> {
StreamController<double> _currentPositionController;
double position = 0;
Timer _progressTimer;
void positionUpdate(Timer timer) {
position += 0.550;
_currentPositionController.add(position);
}
@override
void initState() {
super.initState();
_progressTimer = Timer.periodic(
Duration(milliseconds: 350), positionUpdate);
_currentPositionController = StreamController<double>.broadcast();
}
@override
void dispose() {
_progressTimer?.cancel();
_currentPositionController?.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: TmpPage(currentPosition:_currentPositionController.stream ),
),
);
}
}
This is my widget(TmpPage) :
import 'package:flutter/material.dart';
import 'dart:ui';
class TmpPage extends StatefulWidget {
final Stream<double> currentPosition;
const TmpPage({Key key, this.currentPosition}) : super(key: key);
@override
_TmpPageState createState() => _TmpPageState();
}
class _TmpPageState extends State<TmpPage> with WidgetsBindingObserver {
double before = 0;
double halfWidth;
double leftPosition;
ScrollController _controller;
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
halfWidth =0;
_controller = ScrollController();
_controller.addListener(_scrollListener);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_controller.removeListener(_scrollListener);
_controller.dispose();
super.dispose();
}
double width = 0.0;
double height = 0.0;
@override void didChangeMetrics() {
setState(() {
width = window.physicalSize.width;
height = window.physicalSize.height;
halfWidth = window.physicalSize.width / 2;
//halfWidth = (MediaQuery.of(context).size.width) / 2;
leftPosition = halfWidth;
});
}
@override
Widget build(BuildContext context) {
return _getBody(context);
}
Widget _getBody(context) {
if(halfWidth ==0)
halfWidth = (MediaQuery.of(context).size.width) / 2;
if (before == 0)
leftPosition = halfWidth;
return StreamBuilder<double>(
stream: widget.currentPosition,
builder: (context, snapshot) {
if(snapshot.data !=null && _controller.hasClients){
_controller.jumpTo(snapshot.data);
}
return NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollUpdateNotification) {
var m = scrollNotification.metrics;
before = m.extentBefore;
setState(() {
leftPosition = before;
});
return false;
}
return false;
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SingleChildScrollView(
controller: _controller,
scrollDirection: Axis.horizontal,
child: Row(
children: <Widget>[
SizedBox(
width: halfWidth,
),
Stack(
children: <Widget>[
Row(
children: List.generate(
30,
(i) => Padding(
padding: const EdgeInsets.all(2.0),
child: new Container(
height: 42.0,
width: 42.0,
color: _getColor(i),
),
)).toList(),
),
Positioned(
left: leftPosition,
top: 0,
bottom: 0,
child: Container(
color: Colors.red,
width: 2,
),
),
],
),
SizedBox(
width: halfWidth,
),
],
)),
],
),
);
}
);
}
Color _getColor(int i) {
if(i<10)
return Colors.blueGrey;
if(i<20)
return Colors.orange;
if(i<30)
return Colors.lightBlueAccent;
else
return Colors.green;
}
void _scrollListener() {
if (_controller.offset >= _controller.position.maxScrollExtent &&
!_controller.position.outOfRange) {
print('end');
_controller.jumpTo(_controller.offset - 1);
}
if (_controller.offset <= _controller.position.minScrollExtent &&
!_controller.position.outOfRange) {
print('start');
_controller.jumpTo(_controller.offset + 1);
}
}
}
Upvotes: 0
Views: 540
Reputation: 1577
You cannot trigger a build from within the build method, like with setState
. Thus, you can wrap any calls to setState
with
WidgetsBinding.instance.addPostFrameCallback((_) {
// Your code here
});
in order to call the code at the end of the frame, after the build method completes.
Upvotes: 1