I want to make a minute/second stopwatch that isn't very complex interms of code. I tried using the Stopwatch class that returns it's current value when it's function is called, is there any way i can use the stopwatch class that will automatically update my text widget every second. I am using getx as a state manager.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
const title = 'Grid List';
return MaterialApp(
title: title,
home: Scaffold(
appBar: AppBar(
title: const Text(title),
),
body: MyTimer(),
),
);
}
}
class MyTimer extends StatefulWidget {
const MyTimer({Key? key}) : super(key: key);
#override
_MyTimerState createState() => _MyTimerState();
}
class _MyTimerState extends State<MyTimer> {
final stopWatchClass = Stopwatch();
#override
void initState() {
stopWatchClass.start();
super.initState();
}
#override
Widget build(BuildContext context) {
return Text(stopWatchClass.elapsedMilliseconds.toString());
}
}
0 is written on the Text widget, but if u start the stopwatch a bit earlier, it shows the correct time elapsed. The problem is that i want the text widget to automatically update everytime a second has elapsed, somewhat like listening to the stopWatchClass.
Related
I want to pass deviceWidth and deviceHeight to other screen. Here is my code:
class _SplashPageState extends State<SplashPage> {
#override
void initState(){
super.initState();
var deviceHeight = MediaQuery.of(context).size.height;
var deviceWidth = MediaQuery.of(context).size.width;
Future.delayed(Duration(seconds: 1), () async {
await Navigator.pushNamed(context, "/home");
});
}
But when I run this code, I am getting this error:
dependOnInheritedWidgetOfExactType<MediaQuery>() or dependOnInheritedElement() was called before _SplashPageState.initState() completed.
How to fix this? And the way I'm doing is right way?
The issue is that accessing anything from the context from init state might not guarantee that the widget is already built. The solution is to trigger a method after the first initial render of the frame. That way, the context is properly updated with the current context of that the widget has.
Try this example code i wrote:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) {
var deviceHeight = MediaQuery.of(context).size.height;
var deviceWidth = MediaQuery.of(context).size.width;
print(deviceHeight);
print(deviceWidth);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
);
}
}
This would print the width and height of the screen. Without any errors. This is because i am using
WidgetsBinding.instance!.addPostFrameCallback((_) {
});
Essentially what it does is the method will be executed after the first frame is built. more info here https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html
I would like to add that it's a weird use case to get the screen width and height from within init state. There might be a better approach to what you are trying to achieve.
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);
}
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.
I was following flutter tutorials for managing state of a widget from its parent on this link [https://flutter.dev/docs/development/ui/interactive#parent-managed][1]
and i cant figure out how would call the widget in this case
it is very simple once you get the logic.
In practice, the parent (the "true" widget that you call), i.e.
class ParentWidget extends StatefulWidget {
#override
_ParentWidgetState createState() => _ParentWidgetState();
}
is the one that you call wherever and whenever you want in the rest of the code.
Since this is a Stateful widget, it means that it has stated (to keep it simple, it will manage any changes on the UI). Any change will occur, It will be changing its state and so, this code:
class _ParentWidgetState extends State<ParentWidget> {
bool _active = false;
void _handleTapboxChanged(bool newValue) {
setState(() {
_active = newValue;
});
}
#override
Widget build(BuildContext context) {
return Container(
child: TapboxB(
active: _active,
onChanged: _handleTapboxChanged,
),
);
}
}
Anyhow, once you use a Stateful widget, you change its state whenever you want to call the function
setState(() {
oldValue= newValue;
});
It will rebuild the entire widget changing the stuff you want (such as texts, images, widgets, and so on).
In a non-proper way, consider it as a particular widget that can change its UI during the time.
if you want to call it in MyApp's build method you will have to make MyApp a stateful widget so that it can manage the state of the said widget
void main() => runApp(MyApp());
//we make MyApp to be a stateful widget
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
//we define the state which will be used in the widget here
var myState = "something";
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Container(
//the data used by MyWidget is managed by MyApp which is a statefull widget.
child: MyWidget(state: myState),
),
),
),
);
}
}
Or rather wrap your widget with another stateful widget which you will use in MyApp's build method
//we create a widget which will manage the state of its children class MyStateManagingWidget extends StatefulWidget { #override
_MyStateManagingWidgetState createState() => _MyStateManagingWidgetState(); }
class _MyStateManagingWidgetState extends State<MyStateManagingWidget> { var myState = "some state"; #override Widget build(BuildContext context) {
//we put our widget who's state is to be managed here
return MyWidget(); } }
class MyApp extends StatelessWidget { #override Widget build(BuildContext context) {
return MaterialApp(
title: 'Material App',
home: Scaffold(
appBar: AppBar(
title: Text('Material App Bar'),
),
body: Center(
child: Container(
//we now use the state managing widget here
child: MyStateManagingWidget()),
),
),
); } }
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]}"));
});
}
}