Flutter Hydrated Bloc always replaced with the initial state and not persisting - flutter

I am trying to use hydrated bloc to persist my cubit.
But after some trial and error, I found that the loaded state will always be replaced with the initial state
class DefectSectionsCubit extends HydratedCubit<DefectSectionsState> {
DefectSectionsCubit() : super(DefectSectionsState.initial()) {
print("replaced by init");
}
#override
DefectSectionsState? fromJson(Map<String, dynamic> json) {
print("get from json");
return DefectSectionsState.fromMap(json);
}
#override
Map<String, dynamic>? toJson(DefectSectionsState state) {
return state.toMap();
}
}
the "get from json" do get the last state I saved before,
but it will soon be replaced with the initial state and therefore the state cannot persist.
The output will be:
I/flutter (30280): get from json
I/flutter (30280): replaced by init
you can see the fromJson will be called first and then the constructor, which makes the initial state will always override the loaded state.
My state is like this:
class DefectSectionsState extends Equatable {
final List<DefectSection> defectSections;
const DefectSectionsState({
required this.defectSections,
});
factory DefectSectionsState.initial() => const DefectSectionsState(
defectSections: [
DefectSection(
sectionName: 'FABRIC',
defectItems: [
DefectItem(name: 'Cotton'),
],
),
DefectSection(
sectionName: 'COLOR',
defectItems: [
DefectItem(name: 'Crimson'),
],
),
DefectSection(
sectionName: 'SEWING',
defectItems: [
DefectItem(name: 'Lockstitch'),
],
),
],
);
}
So how to make my state persistent?
Thanks

I am mis-implemented the fromMap in my state.
So, I solve this by imeplement the proper fromMap

Related

Blocbuilder not updating when list in map changes

I'm trying to trigger an update when a list in my map changes. Type is Map<String, List<int>>. Basically one of the integers is changing in the list but not triggering the blocbuilder. Although when I print the state the value is updated. I'm using freezed. From what I understand freezed only provides deep copies for nested #freezed objects but not for Iterables. I've seen a few solutions for this kind of problem. For example create a new Map with Map.from and emit that map. But that doesn't trigger a rebuild. Any suggestions!
My freezed state is
onst factory RiskAttitudeState.loaded({
required int customerId,
required RiskAttitudeQuestionsInfo riskAttitude,
required Map<String, List<int>> answerIds,
#Default(FormzStatus.pure) FormzStatus status,
int? finalRisk,
}) = RiskAttitudeLoaded;
And I'm updating an integer in the list type List<int> in the map answerIds
Here is the bloc
Future _mapAnswerToState(
String id, List<int> answerIds, Emitter<RiskAttitudeState> emit) async {
await state.maybeMap(
loaded: (RiskAttitudeLoaded loaded) async {
if (loaded.answerIds.containsKey(id)) {
loaded.answerIds.update(
id,
(_) => answerIds,
ifAbsent: () {
add(RiskAttitudeEvent.error(Exception('unknown Question ID: $id')));
return answerIds;
},
);
}
emit(loaded.copyWith(answerIds: loaded.answerIds));
},
orElse: () async {},
);
}
For contest if I pass an empty map like this emit(loaded.copyWith(answerIds:{}));
the builder gets triggered.
unfortunality i came accross this problem too. if your algorithm requires change one item of list maybe you can remove this item from your list and then change its properties. after that if you add the item to the list, builder will be triggered..
I tried a small code with cubit and Equatable and it worked. the key note is that you should override props method and add answerIds and other fields if exists to props and all fields must be final.
also notice to use Map<String, List<int>>.from to fill the map.
so the state class looks like this:
class UcHandleState extends Equatable {
final Map<String, List<int>> answerIds;
const UcHandleState({
required this.answerIds,
});
#override
List<Object> get props => [
answerIds,
];
UcHandleState copyWith({
Map<String, List<int>>? answerIds,
}) {
return UcHandleState(
answerIds: answerIds != null
? Map<String, List<int>>.from(answerIds)
: this.answerIds,
);
}
}
and a simple cubit class for managing events is like below. in valueChanged I'm just passing List<int>.
class TestCubit extends Cubit<TestState> {
TestCubit() : super(const TestState(answerIds: {'1': [1, 1]}));
void valueChanged(List<int> newValues ) {
Map<String, List<int>> test = Map<String, List<int>>.from(state.answerIds);
test['1'] = newValues;
emit(state.copyWith(
answerIds: test,
));
}
}
so in UI I call valueChanged() method of cubit:
cubit.valueChanged(newValues:[ Random().nextInt(50), Random().nextInt(70)]);
and the blocBuilder gets triggered:
return BlocBuilder<UcHandleCubit, UcHandleState>(
buildWhen: (prev, cur) =>
prev.answerIds!= cur.answerIds,
builder: (context, state) {
print(state.answerIds.values);
....

Reset values event and state in the bloc flutter

I give the values to the event individually, but the values of the other variables are reset in the event. I did the same for the state. I defined a state with two variables that I gave to those values by copyWith in different places But every time an ion is sent, the state values return to their original state.
Are the states and event in the block reset in each change of its values ????
main
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:test1/bloc/datatest_bloc.dart';
import 'package:test1/bloc/datatest_state.dart';
import 'package:test1/bloc/datatest_event.dart';
import 'bloc/datatest_bloc.dart';
main() {
runApp(MaterialApp(
home: BlocProvider<Databloc>(
create: (context) => Databloc(),
child: Home(),
),
));
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<Databloc, DatatestState>(
builder: (context, state) {
return Center(
child: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<Databloc>(context)
.add(DataEvent_name().copyWith(name: 'mohammd'));
},
child: Text('btn'),
),
ElevatedButton(
onPressed: () {
BlocProvider.of<Databloc>(context)
.add(DataEvent_name().copyWith(pass: 'passsss'));
},
child: Text('pass'),
),
],
),
);
},
),
);
}
}
event-bloc
}
class DataEvent_name extends DatatestEvent {
final String name;
final String pass;
DataEvent_name({ this.name='', this.pass=''});
DataEvent_name copyWith({String? name, String? pass}) {
return DataEvent_name(name: name ?? this.name, pass: pass ?? this.pass);
}
}
class DataEvent_pass extends DatatestEvent {}
class DataEvent_print extends DatatestEvent {}
state-class
class DatatestState {
final String name;
final String pass;
DatatestState({
this.name ='',
this.pass='',
});
DatatestState copyWith({
String? name,
String? pass,
}) {
return DatatestState(
name: name ?? this.name,
pass: pass ?? this.pass,
);
}
}
bloc-class
class Databloc extends Bloc<DatatestEvent, DatatestState> {
Databloc() : super(DatatestState());
final servisApi = ServisApi();
#override
Stream<DatatestState> mapEventToState(DatatestEvent event) async* {
if (event is DataEvent_name) {
yield DatatestState(name: event.name, pass: event.pass);
servisApi.PrintState(a: state.name, b: state.pass);
}
}
}
outpout
flutter: a:mohammd b:
flutter: a: b:passsss
flutter: a:mohammd b:
flutter: a: b:passsss
flutter: a:mohammd b:
flutter: a: b:passsss
BLoC design pattern helps separate the presentation layer from the business logic.
Events are the input to a Bloc. They are commonly UI events such as button presses. Events are dispatched and then converted to States.
States are the output of a Bloc. Presentation components can listen to the stream of states and redraw portions of themselves based on the given state.
Transitions occur when an Event is dispatched after mapEventToState has been called but before the Bloc's state has been updated. A Transition consists of the currentState, the event which was dispatched, and the nextState.
Coming to the example you shown, you have having 2 button in UI:
btn : You are triggering an event DataEvent_name by passing only Name argument as "mohammd". Hence, a final state is having a value as name:'mohammd' and pass:''.
Since you are printing the current state value, hence the output is :
a:mohammd b:
pass: You are triggering an event DataEvent_name by passing only pass argument as "passsss". Hence, a final state is having a value as name: '' and pass:'passsss'. Since you are printing the current state value, hence the output is :
a: b:passsss
Are the states and event in the block reset in each change of its
values
Event are just the input. It is a carrier of data from UI so that required data is available while doing a business logic.
As state are the output of an event, in the entire BLoC it can have only one state at a time. Hence, yes it will update the earlier state.
It depends on the use case if we want to reset entirely or update certain values in the existing state.
How to update existing state
In mapEventToState, we are having access to the current state using state variable. So while yielding a new state, we can pass the data from current state as well based on the required use case.
In your example: If you want to maintain the old value of name & pass if its blank:
yield DatatestState(
name: event.name == '' ? state.name : event.name,
pass: event.pass == '' ? state.pass : event.pass);
It will return the below output:
a:mohammd b:
a:mohammd b:passsss

Flutter: Strange behavour of StreamProvider, widgets rebuilt with incomplete data

I am getting incomplete data from a StreamProvider.
The next minimum widget tree reproduces my problem: A SreamProvider over a Tabbed Screen.
My User object contains a map of several values (among other properties) and I use these values for one screen of the Tabbed View by calling final user = Provider.of<User>(context); inside the build() method in order to get these values in this screen whenever the app starts or is completely rebuilt (i.e. hot reload).
The problem: Whenever I switch tabs and go back to this tab, the build() method is called only once with final user = Provider.of<User>(context); returning an incomplete copy of User: some data is missing (some properties of a map inside the User object) and build() is never called again to complete the data (given that the StreamProvider should return the complete object at some point, possibly causing some rebuilds. The result: some data is missing and some widgets of the Screen cannot be built.
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final firebaseUser = Provider.of<FirebaseUser>(context);
// return either Home or Authenticate widget:
if (firebaseUser == null) {
return WelcomeScreen();
} else {
return StreamProvider<User>.value(
value: FirestoreService(uid: firebaseUser.uid).user,
child: TabsWrapper(),
);
}
}
}
class TabsWrapper extends StatefulWidget {
final int initialIndex;
TabsWrapper({this.initialIndex: 0});
#override
_TabsWrapperState createState() => _TabsWrapperState();
}
class _TabsWrapperState extends State<TabsWrapper> with TickerProviderStateMixin {
TabController tabController;
#override
void initState() {
super.initState();
tabController = TabController(vsync: this, length: choices.length);
}
#override
Widget build(BuildContext context) {
return DefaultTabController(
initialIndex: widget.initialIndex,
length: 3,
child: Scaffold(
backgroundColor: lightBlue,
bottomNavigationBar: TabBar(
controller: tabController,
labelStyle: navi.copyWith(color: darkBlue),
unselectedLabelColor: darkBlue,
labelColor: darkBlue,
indicatorColor: darkBlue,
tabs: choices.map((TabScreen choice) {
return Tab(
text: choice.title,
icon: Icon(choice.icon, color: darkBlue),
);
}).toList(),
),
body: TabBarView(
controller: tabController,
children: <Widget>[
FirstScreen(),
SecondScreen(),
ThirdScreen(),
],
),
),
);
}
}
The problematic Screen (FirstScreen):
class FirstScreen extends StatelessWidget {
final TabController tabController;
final User user;
FirstScreen ();
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
print('**************************************** ${user.stats}'); //Here I can see the problem
return SomeBigWidget(user: user);
}
}
The print(${user.stats}) shows the incomplete map (stats) and the build() is never called again (with the remaining data) so the User object remains incomplete. It is only called twice (and the data returned with the complete object) when reloading or launching the app.
Any solution will be welcome!
PD: I found a simpler way to reproduce the situation. No need to change tabs:
Inside FirstScreen() I have a column of StatelessWidgets. If I call Provider.of<User>(context) within one of them I get an incomplete version of the object. The user.stats map has half of the key-value pairs that it has when accesing it through Provider.of some widgets above.
This is how the stream is updated from Firebase. (the object is created every time):
Stream<User> get user {
Stream<User> userStream = usersCollection.document(uid).snapshots().map((s) => User.fromSnapshot(s));
return userStream;
}
I also have updateShouldNotify = (_,__) => true;
The User model:
class User {
String uid;
String name;
String country;
Map stats;
User.fromUID({#required this.uid});
User.fromSnapshot(DocumentSnapshot snapshot)
: uid = snapshot.documentID,
name = snapshot.data['name'] ?? '',
country = snapshot.data['country'] {
try {
stats = snapshot.data['stats'] ?? {};
} catch (e) {
print('[User.fromSnapshot] Exception building user from snapshot');
print(e);
}
}
}
This is the stats data Map in Firestore:
Although firebase does allow you to use a map as a type for you to use I don't think it is your best option in this case, as it seems to be confusing.
There are multiple solutions to your problem.
The best one I think is to make a collection side by side your users collection called "stats" and have the docid of each stat = the userid of the user with that stats, this makes querying easier for you in the future.
after this you can use the method you have already used to get the stats just like you get the users.
class Stat {
String statid;
int avg;
int avg_score;
List<int> last_day;
int n_samples;
int total_score;
int words;
Stat.fromSnapshot(DocumentSnapshot snapshot){
statid = snapshot.documentID,
avg = snapshot.data['average'] ?? '',
//rest of data
}
}
perhaps the simple solution that you need is to change your map in the User class to this.
Map<String, dynamic> stats = Map<String, dynamic>();
remove the try and catch block, then you should be able to access your data like this user.stats['average'];
hope this helps, I will update my answer when I can actually put this to the test in an emulator, but if you try it and it works let me know.

Flutter: Access SharedPreferences Provider / ChangeNotifier in a Stream Class

I've looked around in StackoverFlow and was not able to find myself a solution to this.
Scenario:
I have a Flutter SharedPreferences Provider with ChangeNotifier Class, that will get updated with the current Logged In User info.
Simplified content:
class SharedPreferences {
final String userId;
final String userName;
SharedPreferences({
#required this.userId,
#required this.userName,
});
}
class SharedPreferencesData with ChangeNotifier {
var _sharedPreferencesData = SharedPreferences(
userId: 'testUserId',
userName: 'testUserName',
);}
And a database.dart file with Class containing DataBaseServices to get FireStore Streams from Snapshots:
class DatabaseService {
final CollectionReference companiesProfilesCollection =
Firestore.instance.collection('companiesProfiles');
List<CompanyProfile> _companiesProfilesFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return CompanyProfile(
docId: doc.documentID,
companyName: doc.data['companyName'] ?? '',
maxLocationsNumber: doc.data['maxLocationsNumber'] ?? 0,
maxUsersNumber: doc.data['maxUsersNumber'] ?? 0,
);
}).toList();
}
Stream<List<CompanyProfile>> get getCompaniesProfiles {
return companiesProfilesCollection
.where('userId', isEqualTo: _userIdFromProvider)
// My problem is above -----
.snapshots()
.map(_companiesProfilesFromSnapshot);
}
}
I Don't want to fetch the entire Stream data as it could be massive for other Streams, I just want to pass the userID under .where('userId', isEqualTo:_userIdFromProvider).
I couldn't access the context in this class to get the data from the Provider
Couldn't send the userId to getCompaniesProfiles getter, as getter don't take parameters
And if I convert this getter to a regular method, I wont be able to send the userID to it, as this has to run under void main() {runApp(MyApp());} / return MultiProvider(providers: [ and By then I cannot call fetch the sharedPreferences with a context that does not contain the provider info ...
Couldn't figure out how to receive the context as a constructor in this class, when I did, I got the following Only static members can accessed in initializers in class DatabaseService.
I'm still a beginner, so I would appreciate if you could share with me the best approach to handle this.
Thank you!
*********** Re-Edited by adding the below: **************
I'm trying to implement the same scenario, here is my code:
Main file:
return MultiProvider(
providers: [
ChangeNotifierProvider<SpData>(
create: (context) => SpData(),
),
ProxyProvider<SpData, DS>(
create: (context) => DS(),
update: (ctx, spData, previousDS) {
print('ChangeNotifierProxyProvider RAN');
previousDS.dbData = spData;
return previousDS;
},
),
],
SP File:
class SP {
final String companyId;
SP({
#required this.companyId,
});
}
class SpData with ChangeNotifier {
var _sPData = SP(
companyId: '',
);
void setCompanyId(String cpID) {
final newSharedPreferences = SP(
companyId: cpID,
);
_sPData = newSharedPreferences;
print('_spData companyId:${_sPData.companyId}');
notifyListeners();
}
String get getCompanyId {
return _sPData.companyId;
}
}
DS file:
class DS with ChangeNotifier {
SpData dbData;
void printCompanyId() {
var companyId = dbData.getCompanyId;
print('companyId from DataBase: $companyId');
}
}
The SpData dbData; inside Class DS does not update. I've added the prints to figure out what is running and what is not. When I run my code, the print function in main.dart file print('ChangeNotifierProxyProvider RAN'); does not run.
What am I missing? Why ChangeNotifierProxyProvider is not being triggered, to update dbData inside DS file? Thanks!
You can use ProxyProvider for this purpose.
ProxyProvider is a provider that builds a value based on other providers.
You said you have a MultiProvider, so I guess you have SharedPreferencesData provider in this MultiProvider and then DatabaseService provider. What you need to do is use ProxyProvider for DatabaseService instead of a regular provider and base it on the SharedPreferencesData provider.
Here is an example:
MultiProvider(
providers: [
ChangeNotifierProvider<SharedPreferencesData>(
create: (context) => SharedPreferencesData(),
),
ProxyProvider<SharedPreferencesData, DatabaseService>(
create: (context) => DatabaseService(),
update: (context, sharedPreferencesData, databaseService) {
databaseService.sharedPreferencesData = sharedPreferencesData;
return databaseService;
},
dispose: (context, databaseService) => databaseService.dispose(),
),
],
child: ...
Here is what happens in the code snippet above:
ProxyProvider calls update everytime SharedPreferencesData changes.
DatabaseService gets its sharedPreferencesData variable set inside update.
Now that you have access to sharedPreferencesData inside the DatabaseService instance, you can do what you want easily.

How to read a value from an object - Unhandled Exception

After days of useless attempts, I'm new with Flutter, I can only ask for help from those who know more than me
I need to read the value of 'G'
that I get from an await that executes a query to a database and returns an eventObject object composed of:
id:
object:
(see better in the figure)
having a futures that returns exactly
return new EventObject (
       id: ...,
       object: gruppiJsonParsing.gruppo);
and gruppiJsonParsing that returns an instance of the Gruppi class with:
final String utente;
final String G;
with:
var groupName = eventObject.object;
I only get:
Instance of 'Gruppi'
then, since as in figure 0 = ['Gruppi']
I would think I could write
Gruppi groupName = eventObject.object;
String groupUser = groupName.G;
But so I get instead
Unhandled Exception: type 'List <Groups'' is not a subtype of type 'Gruppi'
Thanks for any precious suggestions
Aa required here the code of GruppiJsonParsing.dart
import 'package:flutter_client_php_backend/models/Gruppi.dart';
class GruppiJsonParsing {
final String result;
final String message;
final List<Gruppi> gruppo;
GruppiJsonParsing({this.result, this.message, this.gruppo});
factory GruppiJsonParsing.fromJson(Map<String, dynamic> json) {
return GruppiJsonParsing(
result: json['result'],
message: json['message'],
gruppo: parseGruppi(json['gruppo']));
}
static List<Gruppi> parseGruppi(json) {
List<Gruppi> gruppiList = new List<Gruppi>();
for (var log in json) {
gruppiList.add(new Gruppi(G: log['G']));
}
return gruppiList;
}
Map<String, dynamic> toJson() =>
<String, dynamic>{'result': result, 'message': message, 'gruppo': gruppo};
}
If you json string look like this
{"result":"success","message":"Query Successful","gruppo":
[{"utente":null,"G":"utente1"}]}
you can parse with
final eventObject = eventObjectFromJson(jsonString);
print('${eventObject.gruppo[0].g}');
related class
// To parse this JSON data, do
//
// final eventObject = eventObjectFromJson(jsonString);
import 'dart:convert';
EventObject eventObjectFromJson(String str) => EventObject.fromJson(json.decode(str));
String eventObjectToJson(EventObject data) => json.encode(data.toJson());
class EventObject {
String result;
String message;
List<Gruppo> gruppo;
EventObject({
this.result,
this.message,
this.gruppo,
});
factory EventObject.fromJson(Map<String, dynamic> json) => EventObject(
result: json["result"] == null ? null : json["result"],
message: json["message"] == null ? null : json["message"],
gruppo: json["gruppo"] == null ? null : List<Gruppo>.from(json["gruppo"].map((x) => Gruppo.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"result": result == null ? null : result,
"message": message == null ? null : message,
"gruppo": gruppo == null ? null : List<dynamic>.from(gruppo.map((x) => x.toJson())),
};
}
class Gruppo {
dynamic utente;
String g;
Gruppo({
this.utente,
this.g,
});
factory Gruppo.fromJson(Map<String, dynamic> json) => Gruppo(
utente: json["utente"],
g: json["G"] == null ? null : json["G"],
);
Map<String, dynamic> toJson() => {
"utente": utente,
"G": g == null ? null : g,
};
}
full code
import 'package:flutter/material.dart';
// To parse this JSON data, do
//
// final eventObject = eventObjectFromJson(jsonString);
import 'dart:convert';
EventObject eventObjectFromJson(String str) => EventObject.fromJson(json.decode(str));
String eventObjectToJson(EventObject data) => json.encode(data.toJson());
class EventObject {
String result;
String message;
List<Gruppo> gruppo;
EventObject({
this.result,
this.message,
this.gruppo,
});
factory EventObject.fromJson(Map<String, dynamic> json) => EventObject(
result: json["result"] == null ? null : json["result"],
message: json["message"] == null ? null : json["message"],
gruppo: json["gruppo"] == null ? null : List<Gruppo>.from(json["gruppo"].map((x) => Gruppo.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"result": result == null ? null : result,
"message": message == null ? null : message,
"gruppo": gruppo == null ? null : List<dynamic>.from(gruppo.map((x) => x.toJson())),
};
}
class Gruppo {
dynamic utente;
String g;
Gruppo({
this.utente,
this.g,
});
factory Gruppo.fromJson(Map<String, dynamic> json) => Gruppo(
utente: json["utente"],
g: json["G"] == null ? null : json["G"],
);
Map<String, dynamic> toJson() => {
"utente": utente,
"G": g == null ? null : g,
};
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
String jsonString = '{"result":"success","message":"Query Successful","gruppo":[{"utente":null,"G":"utente1"}]}';
void _incrementCounter() {
final eventObject = eventObjectFromJson(jsonString);
print('${eventObject.gruppo[0].g}');
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Output
I/flutter (19854): utente1