I don't want to pass data to text widget if the counterValue number is less than 0. This the code:
main.dart :
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_concepts/cubit/cubit/counter_cubit.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return BlocProvider<CounterCubit>(
create: (context) => CounterCubit(),
child: const MaterialApp(
title: 'flutter_bloc Demo',
debugShowCheckedModeBanner: false,
home: HomePage(),
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Flutter BLoC Concepts DEMO")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text("You have pushed the button this many times:"),
// Bloc Builder
BlocBuilder<CounterCubit, CounterState>(
builder: (context, state) {
if (state.counterValue < 0) {
return NOTHING;
} else {
return Text(
state.counterValue.toString(),
style: Theme.of(context).textTheme.headline4,
);
}
},
),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FloatingActionButton(
onPressed: () {
BlocProvider.of<CounterCubit>(context).decrement();
},
tooltip: "Decrement",
child: const Icon(Icons.remove),
),
FloatingActionButton(
onPressed: () {
BlocProvider.of<CounterCubit>(context).increment();
},
tooltip: "Increment",
child: const Icon(Icons.add),
),
],
),
],
),
),
);
}
}
counter_cubit.dart :
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'counter_state.dart';
class CounterCubit extends Cubit<CounterState> {
CounterCubit() : super(CounterState(counterValue: 0));
void increment() => emit(CounterState(counterValue: state.counterValue + 1));
void decrement() => emit(CounterState(counterValue: state.counterValue - 1));
}
counter_state.dart :
part of 'counter_cubit.dart';
class CounterState {
int counterValue;
CounterState({
required this.counterValue,
});
}
Can i pass nothing to text widget when counterValue number is less than 0. I don't want to show negative numbers, so if I press the decrement button when counterValue = 0 the number displayed is not negative -1,-2... / I want stay at 0. Can i do that
adding if else statement in counter_cubit.dart :
void decrement() {
if (state.counterValue <= 0) {
state.counterValue = 0;
} else {
emit(CounterState(counterValue: state.counterValue - 1));
}
}
Related
I'm new to flutter. I'm trying to make a simple automatically updating time.
I tried with RefreshIndicator but it didn't work for me. What is the correct way to make it update per second? Is it possible to make it update with the setState in the bottomNavigationBar by making recursion function?
enter image description here
import 'dart:async';
import 'package:flutter/material.dart';
int Currentindex = 0 ;
late String time1;
var today = DateTime.now();
String time()
{
today = DateTime.now();
time1 = (today.hour.toString()+" : "+today.minute.toString()+" : "+today.second.toString());
return time1;
}
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner : false ,
home: Firstpage()
,);
}
}
class Firstpage extends StatefulWidget {
const Firstpage({Key? key}) : super(key: key);
#override
_FirstpageState createState() => _FirstpageState();
}
class _FirstpageState extends State<Firstpage> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: Currentindex == 0 ? Column(mainAxisAlignment: MainAxisAlignment.center, children: [ElevatedButton(onPressed: (){
setState(() {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context)
{
return SecondPage();
}
)
);
});
}, child: Text("Click me"))],
) : Currentindex == 1 ? Column(mainAxisAlignment : MainAxisAlignment.center,children: [Text(time(),style: TextStyle(fontSize: 80),)], ):
SizedBox()
) ,
bottomNavigationBar: BottomNavigationBar(items: const [
BottomNavigationBarItem(label: "Icecream",icon: Icon(Icons.icecream , color: Colors.white,)),
BottomNavigationBarItem(label: "Time",icon: Icon(Icons.access_time , color: Colors.white,))],
backgroundColor: Colors.blue,
onTap: (int index){setState(() {
if(Currentindex == 1){today = DateTime.now();;}
Currentindex = index;
});},
),
),
);
}
}
class SecondPage extends StatefulWidget {
const SecondPage({Key? key}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
#override
Widget build(BuildContext context) {
return MaterialApp( debugShowCheckedModeBanner: false,
home: Scaffold( backgroundColor: Colors.grey,
appBar: AppBar(title: Text("Cool"),backgroundColor: Colors.transparent,),
body: Center(
child: Column(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ElevatedButton(onPressed: (){
setState(() {
Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context){return Firstpage();
}
)
);
}
);
}, child: Text("Go back"), style: ElevatedButton.styleFrom(primary: Colors.yellow , onPrimary: Colors.orange),)],
),
),
)
);
}
}
See if it helps. Ideally this kind of widget (that updates all the time) should be in leafs and by themselves, to avoid rebuilding parts of your tree unnecessarily.
class MyWidget extends StatefulWidget {
const MyWidget();
#override
State<StatefulWidget> createState() => _MyWidget();
}
class _MyWidget extends State<MyWidget> {
String lastTime = '';
#override
initState() {
super.initState();
timeUpdate.listen((time) => setState(() => lastTime = time ));
}
String get time => DateTime.now().toString();
Stream<String> get timeUpdate async* {
while(true) {
await Future.delayed(Duration(seconds: 1));
yield time;
}
}
#override
Widget build(BuildContext context) {
return Text(
time,
style: Theme.of(context).textTheme.headline4,
);
}
}
If you're trying to make a digital clock looking thing, try this:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class ClockWidget extends StatelessWidget {
const ClockWidget({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: Stream.periodic(const Duration(seconds: 1)),
builder: (context, snapshot) {
return Text(
DateFormat('HH:mm:ss')
.format(DateTime.now().toLocal())
.toString(),
);
},
);
}
}
I'm new to Flutter and I just want to understand something about stateful widget. Here's a simple code that works perfectly just by switching the text color from red to blue when clicking on a button :
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Color myColor = Colors.red;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("My app")),
body: Column(
children: [
Text(
"Just a simple text",
style: TextStyle(color: myColor),
),
FloatingActionButton(
onPressed: () {
setState(() {
myColor =
(myColor == Colors.red) ? Colors.blue : Colors.red;
});
print(myColor);
},
child: Icon(Icons.home)),
],
));
}
}
My question is : if I get the column outside the stateful widget and call it as a component, how and where should I rewrite the setState function ? I begin with this code and I don't know how to continue :
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Color myColor = Colors.red;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("My app")),
body: HomePage());
}
}
Column HomePage()
{
return Column(
children: [
Text(
"Just a simple text",
style: TextStyle(color: myColor), // SHOULD I NOW INJECT myColor AS A PARAMETER OF HomePage ?
),
FloatingActionButton(
onPressed: () {print("WHERE TO PUT THE setState FUNCTION NOW ???")},
child: Icon(Icons.home)),
],
);
}
Your HomePage() is just a function that returns a Column, so you can just include it within the _MyWidgetState class to be able to access the state directly, and call the setState method, like that:
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Color myColor = Colors.red;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("My app")),
body: HomePage());
}
Column HomePage(){
return Column(
children: [
Text(
"Just a simple text",
style: TextStyle(color: myColor), // SHOULD I NOW INJECT myColor AS A PARAMETER OF HomePage ?
),
FloatingActionButton(
onPressed: () {
setState(() {
myColor = Colors.amber;
});
},
child: Icon(Icons.home)),
],
);
}
}
Here's a example class for how to pass data from one class to another class
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'main1.dart';
void main() {
runApp(MaterialApp(
home: Modalbtn(),
));
}
class Modalbtn extends StatefulWidget {
#override
_ModalbtnState createState() => _ModalbtnState();
}
class _ModalbtnState extends State<Modalbtn> {
String value = "0";
// Pass this method to the child page.
void _update(String newValue) {
setState(() => value = newValue);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
IconButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Container(
height: 200,
child: Column(
children: [StatefulModalbtn(update: _update)],
),
);
});
},
icon: Icon(Icons.add),
iconSize: 20,
),
Text(
value,
style: TextStyle(fontSize: 40),
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
class StatefulModalbtn extends StatelessWidget {
final ValueChanged<String> update;
StatefulModalbtn({required this.update});
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => update("100"), // Passing value to the parent widget.
child: Text('Update (in child)'),
);
}
}
If you insist of having the HomePage() function outside the class you could do this for example:
class MyWidget extends StatefulWidget {
MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
Color myColor = Colors.red;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("My app")),
body: HomePage(myColor, changeColor));
}
void changeColor(Color color) {
setState((){
myColor = color;
});
}
}
Column HomePage(Color color, ValueSetter<Color> change)
{
return Column(
children: [
Text(
"Just a simple text",
style: TextStyle(color: color),
),
FloatingActionButton(
onPressed: () { change(Colors.blue);},
child: Icon(Icons.home)),
],
);
}
Here is my source code: https://github.com/liou-jia-hao/flutter_demo_app/tree/Cannot-refresh
I create a file which contains my counter model called "counter.dart", here is code:
import 'package:flutter/foundation.dart';
import 'package:nanoid/nanoid.dart';
class Counter {
String id;
int count;
void increment() {
count++;
}
void decrement() {
count--;
}
Counter(this.id, this.count);
}
class CountersModel with ChangeNotifier {
Map<String, Counter> countersMap = {};
void createCounter() {
var id = nanoid();
countersMap[id] = Counter(id, 0);
notifyListeners();
}
}
And here is my main.dart code:
// ignore_for_file: public_member_api_docs, lines_longer_than_80_chars
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'models/counter.dart';
/// This is a reimplementation of the default Flutter application using provider + [ChangeNotifier].
void main() {
runApp(
/// Providers are above [MyApp] instead of inside it, so that tests
/// can use [MyApp] while mocking the providers
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CountersModel()),
],
child: const MyApp(),
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
themeMode: ThemeMode.dark,
theme: ThemeData(brightness: Brightness.dark),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var countersSet = context.select<CountersModel, Set<Counter>>(
(model) => model.countersMap.values.toSet());
var countersModel = context.read<CountersModel>();
return Scaffold(
appBar: AppBar(
title: const Text('Example'),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: countersSet.map((counter) => BlueButton(counter)).toList(),
),
),
floatingActionButton: FloatingActionButton(
key: const Key('increment_floatingActionButton'),
/// Calls `context.read` instead of `context.watch` so that it does not rebuild
/// when [Counter] changes.
onPressed: countersModel.createCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
class BlueButton extends StatelessWidget {
const BlueButton(this.counter, {Key? key}) : super(key: key);
final Counter counter;
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: counter.increment,
onLongPress: counter.decrement,
child: Column(
children: [Text(counter.id), Text('${counter.count}')],
),
);
}
}
I expected the number on the BlueButton can increase immediately.
But the number on the BlueButton can't increase immediately.
I believe that your counter needs context in order for the button to change.
Take a look at this answer.
I am displaying the weight of an instance of a person class on my homepage. When I update the weight of this instance through a form in a popup bottom sheet the displayed weight is only changed after a hot reload. How can I trigger a setState in my person class when its instances parameters are changed in homepage?
main.dart
import 'package:flutter/material.dart';
import 'package:metricwidget/screens/homepage.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// Root of application
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Homepage(),
);
}
}
person.dart
import 'package:flutter/material.dart';
class person extends StatefulWidget {
int? weight;
person({Key? key, this.weight}) : super(key: key);
void updateWeight(newWeight){
weight = newWeight;
}
#override
_personState createState() => _personState();
}
class _personState extends State<person> {
#override
Widget build(BuildContext context) {
return Center(
child: Text(
widget.weight.toString(),
style: const TextStyle(fontSize: 24),
),
);
}
}
homepage.dart
import 'package:mvs/person.dart';
import 'package:flutter/material.dart';
class Homepage extends StatefulWidget {
const Homepage({Key? key}) : super(key: key);
#override
_HomepageState createState() => _HomepageState();
}
class _HomepageState extends State<Homepage> {
var joe = person(weight: 23);
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Material(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: joe,
),
OutlinedButton(
onPressed: () {
showModalBottomSheet(
context: context,
builder: (context) {
return Form(
key: _formKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12.0),
child: TextFormField(
onSaved: (String? value) {
if (int.parse(value!) > 0) {
setState(() {
joe.updateWeight(int.parse(value));
});
}
},
keyboardType: TextInputType.number,
maxLength: 3,
initialValue: joe.weight.toString(),
decoration: const InputDecoration(
icon: Icon(Icons.label),
),
validator: (value) {
if (value!.isEmpty) {
return "Please enter value";
}
return null;
},
),
),
OutlinedButton(
onPressed: () {
_formKey.currentState!.save();
Navigator.pop(context);
},
child: const Text("submit"),
)
],
),
);
},
);
},
child: const Text("Update"),
)
],
),
);
}
}
Was able to solve this using provider and changenotifier, same as the format outlined in the docs below
Reference: https://pub.dev/packages/provider
I want to fetch data from an API and set those data to the central state(provider) after creating a screen.( similar scenario of react useEfect function)
class MyApp2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Counter>(
child: MyHomePage(title: 'Flutter Demo Home Page'),
create: (BuildContext context) => Counter());
}
}
class _MyHomePageState extends State<MyHomePage> {
void _incrementCounter(dynamic count) {
count.incrementCounter();
}
int fetchData() {
//api request code
return data; // return fetched data
}
#override
Widget build(BuildContext context) {
final count = Provider.of<Counter>(context);
count.setCounter(fetchData());
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${count.counter}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => _incrementCounter(count),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class Counter with ChangeNotifier{
int counter= 0;
void setCounter(int x){
counter =x;
notifyListeners();
}
void clearCounter(){
counter =0;
notifyListeners();
}
void incrementCounter(){
counter++;
notifyListeners();
}
}
It throws and exception and it doesn't work.
setState() or markNeedsBuild() called during build.
If I remove the notifyListeners() function, the app runs without any exceptions but the widget what I want to rebuild isn't rebuilt.
void setCounter(int x){
counter =x;
// notifyListeners();
}
What is the best way to do that?
I am also new to Provider. So this may not be a good solution.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp2(),
);
}
}
class MyApp2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<Counter>(
child: MyHomePage(title: 'Flutter Demo Home Page'),
create: (BuildContext context) => Counter(),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Consumer<Counter>(
builder: (context, counter, _) {
if (counter.waiting)
return CircularProgressIndicator();
else
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Consumer<Counter>(
builder: (context, counter, _) {
return Text(
'${counter.counter}',
style: Theme.of(context).textTheme.display1,
);
},
),
],
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: Provider.of<Counter>(context).incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
class Counter with ChangeNotifier {
int _counter;
bool _waiting;
Counter(){
_waiting = true;
_fetchCounterFromApi();
}
Future<void>_fetchCounterFromApi() async{
_counter = await Future<int>.delayed(Duration(seconds: 2),() => 4);//Do Api request;
_waiting = false;
notifyListeners();
}
int get counter => _counter;
bool get waiting => _waiting;
void incrementCounter() {
_counter++;
notifyListeners();
}
}