So, I have a AnimatedPositioned widget in my widget tree, which contains a form. I want the AnimatedPositioned widget to slide up from bottom when user navigates to the screen. Now, there are many tutorials which show how to do this when user clicks a button using the setState method. But how do I trigger this automatically when this screen is loaded?
The "right" way to do that would be not to use an implicit animation widget like AnimatedPositioned and instead use an explicit animation. Where you start your AnimationController in the initState of a StatefulWidget.
But just technically you could set the "destination" values for your AnimatedPositioned widget in a Future so that the value changes on the next frame. You try remoing the Future to see that otherwise the widget renders at its end position.
This way is not recommended but possible:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _dist = 0.0;
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(children: [
AnimatedPositioned(
child: Text('Hello Words'),
duration: Duration(seconds: 5),
left: _dist,
top: _dist,
),
]),
);
}
#override
initState() {
super.initState();
Future(() {
setState(() {
_dist = 250.0;
});
});
}
}
Related
A very simple timer written in Flutter. But the timer's time is jumping from 1 to 3 , 3 to 6 (crazy) and it goes to -(minus) value when it should stop. Using setState to update the time.
Here is the code
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Timer Demo',
home: MyHomePage(title: 'My Timer'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void updateUI()=>setState((){});
var timer=MyTimer();
#override
Widget build(BuildContext context) {
timer.start(updateUI);
return Scaffold(
appBar: AppBar(
title: Text('Timer'),
),
body: Center(
child: Text(timer.time.toString()),
),
);
}
}
class MyTimer{
int time=10;
start(Function callback){
Timer.periodic((Duration (seconds:1)),(timer){
time--;
callback();
if(time<1) timer.cancel();
});
}
}
I am building the app for windows desktop
You are starting timer inside the build() method, meaning every time the UI is rerendered, the start() function is called inside MyTimer class.
You can try calling the timer.start(updateUI) inside the initState method (add it to _MyHomePageState), e.g:
#override
void initState() {
super.initState();
timer.start(updateUI);
}
I have a StatelessWidget in flutter that has a ScrollController , In a child of this component I have StatefulWidget that add scrollController.addListener in its initState
class MyStatelessWidget extends StatelessWidget {
final ScrollController scrollController = ScrollController();
#override
Widget build(BuildContext context) {
return Column(
children: [
Placeholder(), //Complex child
Placeholder(), //Complex child
Placeholder(), //
MyStatefulWidget(
scrollController: scrollController,
) // Complex child
],
);
}
}
class MyStatefulWidget extends StatefulWidget {
final ScrollController scrollController;
const MyStatefulWidget({
#required this.scrollController,
});
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
#override
Widget build(BuildContext context) {
return Container();
}
#override
void initState() {
print('Scroll listener added');
widget.scrollController.addListener(() {
print('Scroll position changed');
});
super.initState();
}
}
My problem is, When build of parent of MyStatelessWidget executed,sometimes I lost scroll listener
Why? and how can I fix it?
_myStatefulWidget is calling build but not initState. Therefore, you are not registering a listener on the new controller.
The solution would be to use a state management solution in order to share the controller between the widgets. Check out the Provider package.
Below is the minimal version of what i'm attempting to do with my app that maintains the issue. The slider still responds to being interacted with by showing it's animation, however it does not change the value or actually move. This only seems to occur because the Scaffold has been separated out to it's own widget.
I know that changing it to a stateless widget would fix it in this case, however the version in my app requires state changes.
I've tried adding UniqueKeys up and down the widget tree with no luck, though i'll admit I don't fully understand their use in this case.
Any advice, or a point in the right direction? Should I keep playing around with keys, or am I going about this in the wrong way?
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scaffold Test',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage();
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double _value = 20.0;
#override
Widget build(BuildContext context) {
return MyScaffold(
child: Center(
child: Slider(
min: 0,
max: 100,
value: _value,
onChanged: (double value) {
setState(() {
_value = value;
});
},
),
),
);
}
}
class MyScaffold extends StatefulWidget {
final Widget child;
MyScaffold({this.child});
#override
State<StatefulWidget> createState() => new MyScaffoldState(child);
}
class MyScaffoldState extends State<MyScaffold> {
Widget child;
MyScaffoldState(this.child);
#override
Widget build(BuildContext context) {
return Scaffold(
body: child,
);
}
}
As commented by user #pskink, removing the state constructor fixes the issue.
class MyScaffold extends StatefulWidget {
final Widget child;
MyScaffold({this.child});
#override
State<StatefulWidget> createState() => MyScaffoldState();
}
class MyScaffoldState extends State<MyScaffold> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: widget.child,
);
}
}
In flutter, you should never have constructors for states.
if you need to initialize state properties, use initState.
could you please show me how can I notify my statefull child widget that somewhere in parent user clicks on button?
I have two separate .dart files
in the first file I described main screen widget with FAB
and in the second one I have ListWidget (like RecyclerView)
If user tap on FAB I want notify my ListWidget about it so it can e.g. add one more item.
I have java/android background but it's quite hard for me to change my mind flow.
The first option would be to build the child widget each time you add an item to the list, passing the list as a parameter to the child.
But using streams is a nice way to avoid rebuilding the child widget each time. I think the following code is a good starting point (You could also use a StreamBuilder to build the list leveraging the stream).
In main.dart
import 'dart:async';
import 'package:base_test_project/expanding_list.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
StreamController<int> _controller = StreamController<int>();
int _number = 0;
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new ExpandingList(stream: _controller.stream),
),
floatingActionButton: new FloatingActionButton(
onPressed: () {_controller.add(_number++);},
child: new Icon(Icons.add),
),
);
}
}
In expanding_list.dart
import 'dart:async';
import 'package:flutter/material.dart';
class ExpandingList extends StatefulWidget {
Stream<int> stream;
ExpandingList({this.stream});
#override
_ExpandingListState createState() => _ExpandingListState();
}
class _ExpandingListState extends State<ExpandingList> {
List<int> _myList = [];
#override
void initState() {
super.initState();
widget.stream.listen((number) {
setState(() { _myList.add(number); });
});
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _myList.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.all(15.0), child: Text("Item ${_myList[index]}"));
});
}
}
I have an app that looks like this
class MainScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => MainScreenState();
}
class MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return new Container(
child: FlatButton(
onPressed:() {
// Want pop up to launch
},
),
)
}
}
class PopUp extends StatefulWidget {
PopUp({
this.postId
})
#override
State<StatefulWidget> createState() => PopUpState();
}
class PopUpState extends State<PopUp> {
// api logic
#override
Widget build(BuildContext context) {
return new Dialog()
}
}
Is it possible for me to overlay the PopUp widget on top of the MainScreen Widget without using showDialog ? Since i'm trying to make the PopUp stateful in that it processes and rerenders itself depending on the processed data ?