I'm trying to make a weather app with Flutter. But for some reason, the build() method runs before the initState() method finishes. The thing is, all the state variables are initialized using the setState() method inside the initState(), whose variables are to be used in build() method. I guess the problem is that Flutter is trying to access those state variables before the setState() method, which keeps throwing me the error: A non-null String must be provided to a Text widget. I know these codes are too long to read. But I'd appreciate it if you could help me with this.
import 'package:flutter/material.dart';
import "package:climat/screens/LoadingScreen.dart";
import "package:climat/screens/MainScreen.dart";
import "package:climat/screens/SearchScreen.dart";
void main() {
runApp(
MaterialApp(
theme: ThemeData(
fontFamily: "Open Sans",
),
title: "Climat",
initialRoute: "/",
onGenerateRoute: (RouteSettings routeSettings) {
dynamic routes = <String, WidgetBuilder>{
"/": (context) => LoadingScreen(),
"/index": (context) => MainScreen(),
"/search": (context) => SearchScreen(),
};
WidgetBuilder builder = routes[routeSettings.name];
return MaterialPageRoute(builder: (context) => builder(context));
},
),
);
}
import "package:flutter/material.dart";
import "package:flutter_spinkit/flutter_spinkit.dart";
import "package:climat/services/GeolocatorHelper.dart";
import "package:climat/services/NetworkHelper.dart";
import "package:climat/utilities/constants.dart";
class LoadingScreen extends StatefulWidget {
#override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
Future<void> getWeatherData() async {
Map<String, double> coordinates = await GeolocatorHelper().getGeoData();
final NetworkHelper networkHelper = NetworkHelper(
uri:
"$endPoint&lat=${coordinates["latitude"]}&lon=${coordinates["longitude"]}");
final dynamic data = await networkHelper.getData();
return await Navigator.pushNamed(context, "/index", arguments: data);
}
#override
void initState() {
this.getWeatherData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Climat"),
),
body: SafeArea(
child: Center(
child: SpinKitRing(
color: Colors.redAccent,
),
),
),
);
}
}
import "package:flutter/material.dart";
import "package:climat/services/WeatherHelper.dart";
import "package:climat/services/NetworkHelper.dart";
import "package:climat/utilities/constants.dart";
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
Color backgroundColor;
String cityName;
int temperature;
String status;
String image;
int minTemperature;
int maxTemperature;
int humidity;
Future<void> updateUI({String userInput = null}) async {
dynamic weatherData;
if (userInput == null) {
weatherData = ModalRoute.of(context).settings.arguments;
} else {
final NetworkHelper networkHelper =
NetworkHelper(uri: "$endPoint&q=$userInput");
weatherData = await networkHelper.getData();
}
final int weatherCode = weatherData["weather"][0]["id"];
final WeatherHelper weatherHelper = WeatherHelper(
weatherCode: weatherCode,
);
setState(() {
this.backgroundColor = weatherHelper.getBackgroundColor();
this.cityName = weatherData["name"];
this.temperature = weatherData["main"]["temp"].toInt();
this.status = weatherHelper.getWeatherStatus();
this.minTemperature = weatherData["main"]["temp_min"].toInt();
this.maxTemperature = weatherData["main"]["temp_max"].toInt();
this.humidity = weatherData["main"]["humidity"];
this.image = weatherHelper.getWeatherImage();
});
}
#override
void initState() {
this.updateUI();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: this.backgroundColor,
appBar: AppBar(
title: Text("Climat"),
),
body: SafeArea(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
this.cityName,
style: TextStyle(
fontSize: 24.0,
color: Colors.white,
),
),
SizedBox(
height: 30.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"./assets/images/${this.image}",
scale: 4.0,
),
SizedBox(
width: 30.0,
),
Text(
"${this.temperature}°C",
style: TextStyle(
fontSize: 96.0,
color: Colors.white,
),
),
],
),
SizedBox(
height: 30.0,
),
Text(
this.status.toUpperCase(),
style: TextStyle(
fontSize: 24.0,
color: Colors.white,
),
),
SizedBox(
height: 10.0,
),
Text(
"MIN / MAX : ${this.minTemperature.toString()} / ${this.maxTemperature.toString()}",
style: TextStyle(
fontSize: 24.0,
color: Colors.white,
),
),
SizedBox(
height: 10.0,
),
Text(
"HUMIDITY : ${this.humidity}%",
style: TextStyle(
fontSize: 24.0,
color: Colors.white,
),
),
SizedBox(
height: 30.0,
),
ElevatedButton(
onPressed: () async {
dynamic userInput =
await Navigator.pushNamed(context, "/search");
this.updateUI(
userInput: userInput.toString(),
);
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(
vertical: 8.0,
horizontal: 16.0,
),
),
child: Text(
"Search by city name",
style: TextStyle(
fontSize: 20.0,
),
),
),
],
),
),
),
);
}
}
if you want to use Future function in initState and want it run once and complete before build, please consider to use WidgetsBinding.instance.addPostFrameCallback, like
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await this.getWeatherData();
setState(() { });
});
}
and
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await this.updateUI();
setState(() { });
});
}
setState should be used for rebuild the widget
Related
I'm developing an app to make meal reservations, and I have an ElevatedButton that opens an alert when pressed. This alert is where the user is able to confirm the reservation. So, the alert has 2 text buttons, and I need that when the "sim" button is pressed, the ElevatedButton changes from "Reservar" with green color to "Cancelar Reserva" with red color.
I tried this way but it doesn't work:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../components/meal_item.dart';
import '../models/day_of_week.dart';
import '../models/want_to_comment.dart';
import '../models/meal.dart';
import '../utils/app_routes.dart';
import '../data/dummy_data.dart';
enum StatusReserva { y, n }
Color statusToColor(StatusReserva value) {
Color color = Colors.green;
switch (value) {
case StatusReserva.n:
break;
case StatusReserva.y:
color = Colors.red;
break;
}
return color;
}
String statusToString(StatusReserva value) {
String title = 'Reservar';
switch (value) {
case StatusReserva.n:
break;
case StatusReserva.y:
title = 'Cancelar Reserva';
break;
}
return title;
}
class DaysOfWeekMealsScreen extends StatefulWidget {
final List<Meal> meals;
final StatusReserva status;
final Function(StatusReserva) onStatusChanged;
const DaysOfWeekMealsScreen({
Key? key,
required this.meals,
required this.status,
required this.onStatusChanged,
}) : super(key: key);
#override
State<DaysOfWeekMealsScreen> createState() => _DaysOfWeekMealsScreenState();
}
class _DaysOfWeekMealsScreenState extends State<DaysOfWeekMealsScreen> {
StatusReserva status = StatusReserva();
#override
void initState() {
super.initState();
status = widget.status;
}
#override
Widget build(BuildContext context) {
final dayOfWeek = ModalRoute.of(context)!.settings.arguments as DayOfWeek;
final dayOfWeekMeals = widget.meals.where((meal) {
return meal.days_of_week.contains(dayOfWeek.id);
}).toList();
void _incrementCount(BuildContext context) {
dayOfWeek.count++;
}
Future<void> _showMyDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
' ',
),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text(
'Confirmar reserva para o dia XX/XX/XX?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Raleway',
fontWeight: FontWeight.bold),
),
],
),
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: const Text('Sim'),
onPressed: () => {
_incrementCount,
Navigator.pop(context),
status = StatusReserva.y
},
),
TextButton(
child: const Text('Não'),
onPressed: () => Navigator.pop(context),
),
],
),
],
);
},
);
}
return Scaffold(
appBar: AppBar(
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Image.asset(
'assets/images/logo.png',
fit: BoxFit.contain,
height: 32,
),
Container(
padding: const EdgeInsets.all(8.0),
child: Text(dayOfWeek.title)),
],
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
margin: const EdgeInsets.all(10),
child: ElevatedButton(
onPressed: () => {_showMyDialog(context)},
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
padding: MaterialStateProperty.all(const EdgeInsets.all(15)),
backgroundColor:
MaterialStateProperty.all(statusToColor(widget.status)),
),
child: Text(statusToString(widget.status)),
),
),
Expanded(
child: ListView.builder(
itemCount: dayOfWeekMeals.length,
itemBuilder: (ctx, index) {
return MealItem(dayOfWeekMeals[index]);
},
),
),
],
),
);
}
}
import 'package:apetit_project/models/want_to_comment.dart';
import 'package:apetit_project/screens/login_screen.dart';
import 'package:flutter/material.dart';
import 'screens/tabs_screen.dart';
import 'screens/days_of_week_meals_screen.dart';
import 'screens/meal_detail_screen.dart';
import 'screens/settings_screen.dart';
import 'screens/want_to_comment_screen.dart';
import 'screens/comment_screen.dart';
import 'utils/app_routes.dart';
import 'models/meal.dart';
import 'models/settings.dart';
import 'data/dummy_data.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Settings settings = Settings();
StatusReserva status = StatusReserva();
List<Meal> _gourmetMeals = [];
List<Meal> _lightMeals = [];
void _filterMeals(Settings settings) {
setState(() {
this.settings = settings;
});
}
void _reserveMeals(StatusReserva status) {
setState(() {
this.status = status;
});
}
void _toggleGourmet(Meal meal) {
setState(() {
_gourmetMeals.contains(meal)
? _gourmetMeals.remove(meal)
: _gourmetMeals.add(meal);
});
}
bool _isGourmet(Meal meal) {
return _gourmetMeals.contains(meal);
}
void _toggleLight(Meal meal) {
setState(() {
_lightMeals.contains(meal)
? _lightMeals.remove(meal)
: _lightMeals.add(meal);
});
}
bool _isLight(Meal meal) {
return _lightMeals.contains(meal);
}
#override
Widget build(BuildContext context) {
final ThemeData tema = ThemeData(
fontFamily: 'Raleway',
textTheme: ThemeData.light().textTheme.copyWith(
headline6: const TextStyle(
fontSize: 20,
fontFamily: 'Raleway',
fontWeight: FontWeight.bold,
),
),
);
return MaterialApp(
title: 'Appetit',
theme: tema.copyWith(
colorScheme: tema.colorScheme.copyWith(
primary: const Color.fromRGBO(222, 1, 59, 1),
secondary: const Color.fromRGBO(240, 222, 77, 1),
),
),
routes: {
AppRoutes.LOGIN: (ctx) => LoginScreen(),
AppRoutes.HOME: (ctx) => TabsScreen(_gourmetMeals, _lightMeals),
AppRoutes.WANT_TO_COMMENT: (ctx) => const WantToCommentScreen(),
AppRoutes.COMMENT: (ctx) => const CommentScreen(),
AppRoutes.DAYS_OF_WEEK_MEALS: (ctx) => DaysOfWeekMealsScreen(
meals: DUMMY_MEALS,
status,
_reserveMeals,
),
AppRoutes.MEAL_DETAIL: (ctx) => MealDetailScreen(
_toggleGourmet, _isGourmet, _toggleLight, _isLight),
AppRoutes.SETTINGS: (ctx) => SettingsScreen(settings, _filterMeals),
},
);
}
createMaterialColor(Color color) {}
}
To change the state of a widget you can use setState
enum StatusReserva { y, n }
Color statusToColor(StatusReserva value) {
Color color = Colors.green;
switch (value) {
case StatusReserva.n:
break;
case StatusReserva.y:
color = Colors.red;
break;
}
return color;
}
class StatusTest extends StatefulWidget {
const StatusTest({Key? key}) : super(key: key);
#override
State<StatusTest> createState() => _StatusTestState();
}
class _StatusTestState extends State<StatusTest> {
var status = StatusReserva.n;
Future<void> _showMyDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
' ',
),
content: SingleChildScrollView(
child: ListBody(
children: const <Widget>[
Text(
'Confirmar reserva para o dia XX/XX/XX?',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Raleway',
fontWeight: FontWeight.bold),
),
],
),
),
actions: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
child: const Text('Sim'),
onPressed: () => setState(() {
Navigator.pop(context);
status = StatusReserva.n;
}),
),
TextButton(
child: const Text('Não'),
onPressed: () => setState(() {
Navigator.pop(context);
status = StatusReserva.y;
}),
),
],
),
],
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 100,
height: 100,
child: Container(
color: statusToColor(status),
child: const Center(
child: Text(
'Status color',
style: TextStyle(color: Colors.white),
)),
),
),
ElevatedButton(
onPressed: () {
_showMyDialog(context);
},
child: const Text('Show dialog'),
),
],
),
),
);
}
}
For further clarification
The StatusReserva status = StatusReserva.n; should be in the State class _DaysOfWeekMealsScreenState. And to change it use setState. This value should never change if not inside a setState.
The Sim button onPressed should be like the following (Also with fixes of an unintended Set creation):
TextButton(
child: const Text('Sim'),
onPressed: () {
_incrementCount();
Navigator.pop(context);
setState(() => status = StatusReserva.y);
},
),
Use a boolean value instead of enum like this
bool isCancelReservar = false;
and in the state class before build method add this code :
bool isCancelReservar = false;
#override
void initState(){
isCancelReservar = widget.isCancelReservar;
super.initState();
}
Now you are able to update the value of isCancelReservar on clicking "sim button" using setState method. Your text button should look like this :
TextButton(
child: const Text('Sim'),
onPressed: () {
setState(() {
isCancelReservar = true;
});
_incrementCount,
Navigator.pop(context);
},
),
and your elevated button like this :
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
margin: const EdgeInsets.all(10),
child: ElevatedButton(
onPressed: () => {_showMyDialog(context)},
style: ButtonStyle(
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
)),
padding: MaterialStateProperty.all(const EdgeInsets.all(15)),
backgroundColor:
MaterialStateProperty.all(isCancelReservar ? Colors.red :
Colors.green),
),
child: Text(isCancelReservar ? "Cancelar Reserva" : "Reservar"),
),
),
where you can see the backgroundColor will be red with text "Cancelar Reserva" if the value of isCancelReservar is true else it would be of color green with test Reservar.
I created a program to create tasks, everything works as it should, I made such options as validation, deleting the task, changing the theme. But the problem is that when I restart the app all the tasks are deleted. I want to keep them in storage, temporarily or permanently. And I can't realize it. Someone may have experience as such to implement ?? or examples of similar problems. My goal is that after restarting the application, all tasks remain.
My code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main () {
runApp(MaterialApp(
home: App(),
));
}
class ListItem{
String todoText;
bool todoCheck;
ListItem(this.todoText, this.todoCheck);
}
class _strikeThrough extends StatelessWidget{
final String todoText;
final bool todoCheck;
_strikeThrough(this.todoText, this.todoCheck) : super();
Widget _widget(){
if(todoCheck){
return Text(
todoText,
style: TextStyle(
fontSize: 22.0,
),
);
}
else{
return Text(
todoText,
style: TextStyle(
fontSize: 22.0
),
);
}
}
#override
Widget build(BuildContext context){
return _widget();
}
}
class App extends StatefulWidget{
#override
AppState createState(){
return AppState();
}
}
final ValueNotifier<ThemeMode> _notifier = ValueNotifier(ThemeMode.light);
class AppState extends State<App> {
bool valText = true;
var counter = 0;
var IconsType = Icons.wb_sunny ;
late Color ColorType = Colors.black;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
List<ListItem> WidgetList = [];
#override
void dispose() {
textController.dispose();
popUpTextController.dispose();
super.dispose();
}
#override
void initState() {
addToSP(defaultList).then((_) => getSP());
super.initState();
}
Future<void> addToSP(List<List<ListItem>> tList) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString('graphLists', jsonEncode(tList));
}
void getSP() async {
final prefs = await SharedPreferences.getInstance();
final List<dynamic> jsonData =
jsonDecode(prefs.getString('graphLists') ?? '[]');
WidgetList = jsonData.map<List<ListItem>>((jsonList) {
return jsonList.map<TodoInfo>((jsonItem) {
return ListItem.fromJson(jsonItem);
}).toList();
}).toList();
setState(() {});
}
#override
Widget build(BuildContext context) {
return ValueListenableBuilder<ThemeMode>(
valueListenable: _notifier,
builder: (_, mode, __) {
return MaterialApp(
theme: ThemeData.light(),
darkTheme: ThemeData.dark(),
themeMode: mode, // Decides which theme to show, light or dark.
home: Scaffold(
appBar: AppBar(
title: Text("Список задач"),
actions: <Widget>[
IconButton(
icon: Icon(IconsType,color : ColorType
),
onPressed:() =>
{
if (_notifier.value == ThemeMode.light) {
_notifier.value = ThemeMode.dark,
IconsType = Icons.dark_mode,
ColorType = Colors.white,
} else
{
_notifier.value = ThemeMode.light,
IconsType = Icons.wb_sunny,
ColorType = Colors.black,
}
}
)
],
//backgroundColor: Colors.orange[500],
iconTheme: IconThemeData(
color: Colors.white
),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"Tasks",
style: TextStyle(
fontSize: 70.0,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
IconButton(
color: Colors.black,
iconSize: 70,
constraints: const BoxConstraints(),
padding: EdgeInsets.fromLTRB(30.0, 10.0, 30, 10.0),
icon: const Icon(Icons.add_outlined),
onPressed: () {
if (textController.text.replaceAll(" ", "").isNotEmpty) {
WidgetList.insert(0, new ListItem(textController.text.replaceAll(" ", ""), false));
setState(() {
valText = true;
textController.clear();
});
}
else
{
setState(() {
valText = false;
});
}
},
)
],
),
),
Container(
width: MediaQuery
.of(context)
.size
.height * 0.45,
child: TextField(
style: TextStyle(
fontSize: 22.0,
//color: Theme.of(context).accentColor,
),
controller: textController,
cursorWidth: 5.0,
autocorrect: true,
autofocus: true,
//onSubmitted: ,
),
),
Align(
child:
(valText == false) ?
Align(child: Text(("Задача пустая"),
style: TextStyle(
fontSize: 25.0, color: Colors.red)),
alignment: Alignment.center) :
Align(child: Text((""),),
alignment: Alignment.center)),
Expanded(
child: ReorderableListView(
children: <Widget>[
for(final widget in WidgetList)
GestureDetector(
key: Key(widget.todoText),
child: Dismissible(
key: Key(widget.todoText),
child: CheckboxListTile(
controlAffinity: ListTileControlAffinity.leading,
//key: ValueKey("Checkboxtile $widget"),
value: widget.todoCheck,
title: _strikeThrough(
widget.todoText, widget.todoCheck),
onChanged: (checkValue) {
//_strikethrough toggle
setState(() {
if (!checkValue!) {
widget.todoCheck = false;
}
else {
widget.todoCheck = true;
}
});
},
),
background: Container(
child: Icon(Icons.delete),
alignment: Alignment.centerRight,
color: Colors.redAccent,
),
direction: DismissDirection.endToStart,
movementDuration: const Duration(
milliseconds: 200),
onDismissed: (dismissDirection) { //Delete Todo
WidgetList.remove(widget);
},
),
)
],
onReorder: (oldIndex, newIndex) {
setState(() {
if (newIndex > oldIndex) {
newIndex -= 1;
}
var replaceWiget = WidgetList.removeAt(oldIndex);
WidgetList.insert(newIndex, replaceWiget);
});
},
),
)
],
),
)
);
}
);
}
}
class TodoInfo {
String todoText;
bool todoCheck;
TodoInfo({
required this.todoText,
required this.todoCheck,
});
factory TodoInfo.fromJson(Map<String, dynamic> json) {
return TodoInfo(
todoText: json["todoText"],
todoCheck: json["todoCheck"]);
}
factory TodoInfo.fromMap(Map<String, dynamic> map) => TodoInfo(
todoText: map["todoText"]?? '',
todoCheck: map["todoCheck"] ?? '',
);
Map<String, dynamic> toJson() {
return {
"todoText": todoText,
"todoCheck": todoCheck
};
}
#override
String toString() => '{todoText: $todoText, todoCheck: $todoCheck}';
}
My skrin
Here are some options for persistent memory.
You could use any of the storage functions/plugins available for flutter as per your need or interest.
Some of them are
Seflite
Shared preferences
Hive
Example on how to use Shared Preferences: LINK
suddently from nowhere i came up with provider not re rendering my home page when it's updated. I've inspected it and it IS UPDATED. It has newer data when i change it in firebase but the UI won't re-render showing the new data. That's my code:
Main function
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:my_event_app/pages/HomePage/home_page.dart';
import 'package:my_event_app/pages/Login/login_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// is not restarted.
primarySwatch: Colors.blue,
),
home: const Wrapper(),
),
);
}
}
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.userChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User? user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, userSnapshot) {
if (userSnapshot.hasData) {
Provider.of<UserModel>(context, listen: true)
.fromJson(userSnapshot.data!.data());
return const HomePage();
}
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
});
} else {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
And this is the home page:
import 'package:flutter/material.dart';
import 'package:my_event_app/http/auth/user/sign_out.dart';
import 'package:my_event_app/pages/Create_Event/create_event_page.dart';
import 'package:my_event_app/pages/Onboarding/onboarding_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:my_event_app/widgets/event_card_widget.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Consumer<UserModel>(builder: (context, user, child) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.help_outline, color: Colors.black, size: 30),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const OnboardingPage();
}));
},
),
actions: [
IconButton(
icon: const Icon(
Icons.arrow_forward_ios_sharp,
color: Colors.black,
),
onPressed: () {
signOut();
},
),
],
elevation: 0,
backgroundColor: Colors.white,
),
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
"https://cdnnmundo1.img.sputniknews.com/img/07e5/09/13/1116212032_100:0:1273:1173_1920x0_80_0_0_efb734331af13dfe11ff6d43293c60e2.png"),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.orange[400],
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Center(
child: IconButton(
color: Colors.white,
onPressed: () {
// Navigate to add event widget
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return const CreateEventPage();
}));
},
icon: const Icon(Icons.add),
),
),
),
],
),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
child: Text('Welcome, ${user.name}',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
),
const SizedBox(height: 32),
Container(
padding: const EdgeInsets.all(16),
height: 100,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(alignment: Alignment.center, children: [
SizedBox(
height: 45,
width: 45,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Colors.orange[400]),
value: 14 / 20,
semanticsValue: "14/20",
color: Colors.black,
),
),
const Text("78%",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
]),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Weekly progress",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Text("14/20 tasks completed"),
],
),
const Icon(Icons.bar_chart),
],
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Text("You have 5 tasks for today",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Icon(Icons.calendar_today_outlined)
],
),
),
_renderEvents(user),
],
),
),
),
);
});
}
}
Column _renderEvents(UserModel user) {
return Column(
children: [
for (var event in user.events)
EventCard(
eventId: event,
),
],
);
}
And here's the provider:
import 'package:flutter/material.dart';
class UserModel extends ChangeNotifier {
String _name = '';
String _surnames = '';
String _uid = '';
String _email = '';
List<dynamic> _events = [];
String get name => _name;
String get surnames => _surnames;
String get uid => _uid;
String get email => _email;
List<dynamic> get events => _events;
UserModel();
set name(String value) {
_name = value;
notifyListeners();
}
set surnames(String value) {
_surnames = value;
notifyListeners();
}
set uid(String value) {
_uid = value;
notifyListeners();
}
set email(String value) {
_email = value;
notifyListeners();
}
set events(List<dynamic> value) {
_events = value;
notifyListeners();
}
void addEvent(String event) {
_events.add(event);
notifyListeners();
}
void removeEvent(String event) {
_events.remove(event);
notifyListeners();
}
void updateUser(String name, String uid) {
name = name;
uid = uid;
notifyListeners();
}
void clearUser() {
_name = '';
_uid = '';
notifyListeners();
}
Map<String, dynamic> toJson() {
return {
'name': _name,
'surnames': _surnames,
'uid': _uid,
'email': _email,
'events': _events
};
}
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
} catch (e) {
print(e);
}
}
}
```
As you can see i use Consumer in order to read data and i have a change notifier in the begginig, but it won't re render and show for example new name if i change it in fireabase.
Thank you so much!
You are using fromJson method to update values in UserModel, but it does not call notifyListeners. Add notifyListeners(); to the end of this method:
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
notifyListeners(); // add this
} catch (e) {
print(e);
}
}
Also some other things:
Consider declaring class UserModel with ChangeNotifier instead of class UserModel extends ChangeNotifier.
fromJson methods usually are acting as factory methods, meaning these return a new instance, and don't set members in an existing instance.
Instead of Provider.of<UserModel>(context, listen: true).fromJson(userSnapshot.data!.data()); you can try: context.read<UserModel>().fromJson(userSnapshot.data!.data());. Here you don't really need listening, you just want to find the provider and call fromJson. Your Consumer is the one which is listening to the changes accordingly.
I am using BLE esp32 to get data from sensors but when I want to calibrate by getting data from the BLE device it gives me this error on Stream Builder I have used in the Device Setting up screen. A soon as I press Configuration it moves to the device set up page. I want is that, until I get value 1 from BLE it should keep on showing this screen
and once 1 comes it should go to the other screen to show a message and picture done like this and then automatically moves to Homepage after 2 seconds.
But it shows the bottom overflowed with the error msg "Bluetooth is not active", how can I avoid that? and jump for the next page? BLE is active as well since I checked the values were coming on another screen, but it is not happening on this screen for the connected device. Also the device done page it is not even going to Homescreen
I am new to FLutter that's why i have no idea what i should do with it.
Code for Device Set up Screen:
import 'dart:typed_data';
import 'package:epicare/ConfDeviceDone.dart';
import 'package:epicare/ConfigurationScreen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'Homepage.dart';
class ConfDeviceSet extends StatefulWidget {
const ConfDeviceSet({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_ConfDeviceSetState createState() => _ConfDeviceSetState();
}
class _ConfDeviceSetState extends State<ConfDeviceSet> {
// BLE
final String SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
//final String SERVICE_UUID = "0365a300-8d69-4066-80c7-554298a6ec5e";
//final String CHARACTERISTIC_UUID = "cf01c075-cb75-4dea-819e-2a79dd466bcb";
final String CHARACTERISTIC_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
bool isReady;
Stream<List<int>> stream;
List<int> lastValue;
List<double> traceDust = List();
#override
void initState() {
isReady = false;
discoverServices();
}
discoverServices() async {
List<BluetoothService> services = await widget.device.discoverServices();
services.forEach((service) {
if (service.uuid.toString() == SERVICE_UUID) {
service.characteristics.forEach((characteristic) {
if (characteristic.uuid.toString() == CHARACTERISTIC_UUID) {
characteristic.setNotifyValue(!characteristic.isNotifying);
stream = characteristic.value;
//print(stream);
lastValue = characteristic.lastValue;
//print(lastValue);
setState(() {
isReady = true;
});
}
});
}
});
}
_dataParser(List<int> data) {
var value = Uint8List.fromList(data);
print("stream.value: $value"); // stream.value: [33]
var hr = ByteData.sublistView(value, 0, 1);
print("Heart rate: ${hr.getUint8(0)}");
return hr.getUint8(0); // Heart rate: 33
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: const Color(0xffE5E0A1),
elevation: 0,
centerTitle: true,
title: Text(
"Configuration",
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontFamily: 'Montserrat',
fontWeight: FontWeight.normal,
),
),
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: Colors.black,
),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ConfigurationScreen();
},
),
);
},
),
),
body: Column(
children: [
Container(
padding: EdgeInsets.only(top: 65),
alignment: Alignment.center,
child: Text(
'Device setting up...',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
fontSize: 17,
color: const Color(0xa8000000),
height: 1.3333333333333333,
),
textHeightBehavior:
TextHeightBehavior(applyHeightToFirstAscent: false),
textAlign: TextAlign.center,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 120),
child: const SpinKitFadingCircle(
color: const Color(0xffF1C40E),
size: 200,
),
),
StreamBuilder<List<int>>(
stream: stream,
initialData: lastValue,
builder: (BuildContext context, AsyncSnapshot<List<int>> snapshot) {
var currentValue = _dataParser(snapshot.data);
if (currentValue == 1) {
print("Test Conf 1");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return ConfDeviceDone(
device: widget.device,
);
},
),
);
}
},
),
],
),
);
}
}
Code for Device Done screen
import 'package:epicare/Homepage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'ConfigurationScreen.dart';
class ConfDeviceDone extends StatefulWidget {
const ConfDeviceDone({Key key, this.device}) : super(key: key);
final BluetoothDevice device;
#override
_ConfDeviceDoneState createState() => _ConfDeviceDoneState();
}
class _ConfDeviceDoneState extends State<ConfDeviceDone> {
#override
void initState() {
// isReady = false;
// discoverServices();
var d = Duration(seconds: 2);
// delayed 5 seconds to next page
Future.delayed(d, () {
// to next page and close this page
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) {
return Homepage(device: widget.device);
},
),
(route) => false,
);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: const Color(0xffE5E0A1),
elevation: 0,
centerTitle: true,
title: Text(
"Configuration",
style: TextStyle(
fontSize: 15.0,
color: Colors.black,
fontFamily: 'Montserrat',
fontWeight: FontWeight.normal,
),
),
),
body: Column(
children: [
Container(
padding: EdgeInsets.only(top: 65),
alignment: Alignment.center,
child: Text(
'Device Configured Successfully!',
style: TextStyle(
fontFamily: 'Montserrat',
fontWeight: FontWeight.w600,
fontSize: 15,
color: const Color(0xa8000000),
height: 1.3333333333333333,
),
textHeightBehavior:
TextHeightBehavior(applyHeightToFirstAscent: false),
textAlign: TextAlign.center,
),
),
Container(
padding: EdgeInsets.symmetric(vertical: 120),
child: Image.asset('assets/images/green-tick.gif'),
),
],
),
);
}
}
I'm totally new in flutter just learned last weeks, and facing two problem here, I'm trying to solve the error, but still can't able to solve it, can anyone help on me? Below is the dart code.
'main.dart'
class Sharer extends StatefulWidget
{
bool _isLoggedIn = false;
Map userProfile;
final facebookLogin = FacebookLogin();
#override
Widget build(BuildContext context)
{
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.amber,
centerTitle: true,
title: Text('Welcome Sharer',
style: TextStyle(
fontSize: 16.0,
color: Colors.black87,
letterSpacing: 1.0,
),
),
),
body:
//Center(
//child: isLoggedin
//?
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(20.0),
child: RaisedButton.icon(
onPressed: () async {
final result = await facebookLogin.logIn(['email']);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
final token = result.accessToken.token;
final graphResponse = await http.get('https://graph.facebook.com/v2.12/me?fields=name,picture,email&access_token=${token}');
final profile = JSON.jsonDecode(graphResponse.body);
print(profile);
setState(() {
userProfile = profile;
_isLoggedIn = true;
});
break;
case FacebookLoginStatus.cancelledByUser:
setState(() => _isLoggedIn = false );
break;
case FacebookLoginStatus.error:
setState(() => _isLoggedIn = false );
break;
}
},
icon: Icon(
MyFlutterApp.facebook,
),
label: Text(
'Sharer',
style: TextStyle(
fontFamily: 'MyFlutterApp',
color: Colors.black87,
letterSpacing: 1.0,
),
),
),
),
),
],
),
//),
);
}
}
The error was shown at the class name say 'Missing concrete implementation of statefulwidget.create state' another is 'setstate isn't define', i do not know what are the problem can someone help out?
thanks and regards.
Here is an example of a StatefulWidget implementation
class NameOfYourWidget extends StatefulWidget {
#override
_NameOfYourWidgetState createState() => _NameOfYourWidgetState();
}
class _NameOfYourWidgetState extends State<NameOfYourWidget> {
#override
Widget build(BuildContext context) {
return Container(
);
}
}
For your code , use this instead :
class Shared extends StatefulWidget {
#override
_SharedState createState() => _SharedState();
}
class _SharedState extends State<Shared> {
bool _isLoggedIn = false;
Map userProfile;
final facebookLogin = FacebookLogin();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.amber,
centerTitle: true,
title: Text(
'Welcome Sharer',
style: TextStyle(
fontSize: 16.0,
color: Colors.black87,
letterSpacing: 1.0,
),
),
),
body:
//Center(
//child: isLoggedin
//?
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 1,
child: Container(
padding: EdgeInsets.all(20.0),
child: RaisedButton.icon(
onPressed: () async {
final result = await facebookLogin.logIn(['email']);
switch (result.status) {
case FacebookLoginStatus.loggedIn:
final token = result.accessToken.token;
final graphResponse = await http.get(
'https://graph.facebook.com/v2.12/me?fields=name,picture,email&access_token=${token}');
final profile = JSON.jsonDecode(graphResponse.body);
print(profile);
setState(() {
userProfile = profile;
_isLoggedIn = true;
});
break;
case FacebookLoginStatus.cancelledByUser:
setState(() => _isLoggedIn = false);
break;
case FacebookLoginStatus.error:
setState(() => _isLoggedIn = false);
break;
}
},
icon: Icon(
MyFlutterApp.facebook,
),
label: Text(
'Sharer',
style: TextStyle(
fontFamily: 'MyFlutterApp',
color: Colors.black87,
letterSpacing: 1.0,
),
),
),
),
),
],
),
//),
);
}
}
You can learn more about statefulwidget here :
Videos :
Google Developers : How Stateful Widgets Are Used Best
Flutter Tutorial for Beginners- Stateful Widgets
Article : Let's build a simple Stateful Widget in Flutter
Hope it will be helpful