Unable to navigate to homepage after register with firebase Flutter - flutter

Im trying to navigate to homepage after login, this works, i followed this one tutorial:
https://www.youtube.com/watch?v=oJ5Vrya3wCQ&t=307s
But, my problem is when i go to register button > register an user and it takes me straight to the login page but when I fill in the login forms and log in, nothing happens, i must to do a "hot restart" to see my homepage, it is as if some widget did not rebuild. So i should do any better clean way to do this statemanagement?
Main:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:plantsapp/screens/login_page.dart';
import 'package:plantsapp/screens/plant_screen.dart';
import 'package:plantsapp/screens/register_page.dart';
import 'package:plantsapp/screens/shop_scree_page.dart';
import 'package:plantsapp/services/authentication_service.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<AuthenticationService>(
create: (_) => AuthenticationService(FirebaseAuth.instance),
),
StreamProvider(create: (context) => context.read<AuthenticationService>().authStateChanges)
],
child: MaterialApp(
title: 'Flutter Plant Shop UI',
debugShowCheckedModeBanner: false,
home: AuthenticationWrapper(),
routes: {
'registerpage': (context) => RegisterPage(),
'loginpage': (context) => LoginPage(),
'homepage': (context) => PlantScreen(),
},
//theme: ThemeData.dark(),
),
);
}
}
// ignore: must_be_immutable
class AuthenticationWrapper extends StatelessWidget with ChangeNotifier{
#override
Widget build(BuildContext context) {
final firebaseuser = context.watch<User>();
notifyListeners();
if(firebaseuser != null) {
notifyListeners();
return ShopScreen();
} else {
notifyListeners();
return LoginPage();
}
}
}
Authentication service:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
class AuthenticationService with ChangeNotifier{
final FirebaseAuth _firebaseAuth;
AuthenticationService(this._firebaseAuth);
Stream<User> get authStateChanges => _firebaseAuth.authStateChanges();
Future<String> signIn({String email, String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
notifyListeners();
return "Signed in";
} on FirebaseAuthException catch (e) {
print(e);
return e.message;
}
}
Future<String> signUp({String email, String password, BuildContext context}) async{
try {
await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
print('USUARIO REGISTRADO');
Navigator.pushReplacementNamed(context, 'loginpage');
return "Signed Up";
} on FirebaseAuthException catch (e) {
print(e.message);
return e.message;
}
}
Future<void> signOut() async{
await _firebaseAuth.signOut();
notifyListeners();
}
}
LoginPage:
import 'package:flutter/material.dart';
import 'package:plantsapp/services/authentication_service.dart';
import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget{
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage>{
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 80),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 80),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Login',
style: TextStyle(fontSize: 20),
),
_emailForm(),
_passwordForm(),
_button(),
_buttonRegister(),
],
),
),
),
);
}
Widget _emailForm() {
return TextField(
controller: emailController,
decoration: InputDecoration(hintText: ('Email')),
);
}
Widget _passwordForm() {
return TextField(
controller: passwordController,
decoration: InputDecoration(hintText: ('Password')),
);
}
Widget _button() {
return RaisedButton(
child: Text('Login'),
onPressed: () {
Provider.of<AuthenticationService>(context, listen: false).signIn(
email: emailController.text.trim(),
password: passwordController.text.trim());
},
);
}
_buttonRegister() {
return RaisedButton(
child: Text('Registrarse'),
onPressed: () {
Navigator.pushReplacementNamed(context, 'registerpage');
},
);
}
}
RegisterPage:
import 'package:flutter/material.dart';
import 'package:plantsapp/services/authentication_service.dart';
import 'package:provider/provider.dart';
class RegisterPage extends StatefulWidget {
RegisterPage({Key key}) : super(key: key);
#override
_RegisterPageState createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 20, vertical: 80),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 80),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Registrarse',
style: TextStyle(fontSize: 30),
),
_emailForm(),
_passwordForm(),
_button(),
],
),
),
),
);
}
Widget _emailForm() {
return TextField(
controller: emailController,
decoration: InputDecoration(hintText: ('Email')),
);
}
Widget _passwordForm() {
return TextField(
controller: passwordController,
decoration: InputDecoration(hintText: ('Password')),
);
}
Widget _button() {
return RaisedButton(
child: Text('Registrarse'),
onPressed: () {
Provider.of<AuthenticationService>(context, listen: false).signUp(
email: emailController.text.trim(),
password: passwordController.text.trim(),
context: context);
},
);
}
}

Basically, I would say that using provider package in this simplest scenario is not really necessary. Handling authentication state with Firebase can be easily achieved with StreamBuilder and FirebaseAuth.instance.authStateChanges().
So instead of pushing pages with navigator and calling notifyListeners(), just make a streamBuilder which rebuilds whenever authentication state changes (firebase auth package handles simplest state management for you, providing you handy stream of User objects)
StreamBuilder<User>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.data == null) return LoginScreen();
return YourScreenForSignedUsersScreen();
})

i found the problem and i want to share it with all. The problem was the navigator, you have to maintain the flow, my app starts on the LoginPage so i use Navigator to go to RegisterPage, so, i start the app in the RegisterPage without using any Navigator between and test it, and its sends me to the HomePage automatically.

Related

Future<void>.value();

Im having an issue while running the app. after initializing the app I couldnt properly see the screen at the same tiem there was an error shows which is **Future.value(); inside the feedback.dart. I have tried to catch any error which I have added but I couldnt find any.
I have seen many solution also tried but didnt worked out. Can I anyone please Let me know error which I showed below.
This is loginPage file:
import 'package:flutter/material.dart';
import 'package:food_app/bottombar/screenA.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
static Future<User?>loginUsingEmailPassword({required String email, required String password, required BuildContext context}) async {
FirebaseAuth auth = FirebaseAuth.instance;
User? user;
try {
UserCredential userCredential = await auth.signInWithEmailAndPassword(email: email, password: password);
user = userCredential.user;
} on FirebaseAuthException catch (e) {
if(e.code == "user not found"){
print("No User found for that email");
}
}
return user;
}
#override
Widget build(BuildContext context) {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: const Text("Hello world"),
),
body: SizedBox(
height: 400.0,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(15.0),
child: TextFormField(
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Username",
),
),
),
const SizedBox(
height: 20.0,
),
Padding(
padding: const EdgeInsets.all(15.0),
child: TextFormField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Password",
),
),
),
const SizedBox(
height: 20.0,
),
ElevatedButton(
onPressed: () async{
User? user = await loginUsingEmailPassword(email: emailController.text, password: passwordController.text, context: context);
print(user);
if (user != null) {
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => const ScreenA()));
}
},
child: const Text("Login")),
],
),
),
);
}
}
And this is the main.dart file:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:food_app/loginpage.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<FirebaseApp> _initializeFirebase() async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _initializeFirebase(),
builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.done) {
return const LoginPage();
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
);
}
}
And my error is :
context.findRenderObject()!.sendSemanticsEvent(const TapSemanticEvent());
switch (_platform(context)) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return SystemSound.play(SystemSoundType.click);
case TargetPlatform.iOS:
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
return Future<void>.value();
}
}

Login with Facebook not working as expected - flutter

I'm trying to implement login with Facebook in my app which it's "working" but not necessarily as expected, I'll write down the issues I'm having.
When I try to login, I don't get redirected to the homepage and the logs show an error this error:
W/Firestore(27484): (24.1.2) [Firestore]: Listen for Query(target=Query(users where Email==random_email#gmail.com order by name);limitType=LIMIT_TO_FIRST) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
E/flutter (27484): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.
I'm redirected to the login page again and the login only works in the second try.
When I sign out it looks like it works fine because I got redirected to the login page but the logs show the same error above.
For last some times it happens that if I exit the app(by pressing back button) and then re-open I get redirected to the home page instead of the login page it doesn't happen always so not sure what is causing that. I'm displaying the user data in the home page and in this case it's empty.
I was thinking that it could be related to my rules set in firebase but I'm not really sure , I would appreciate it very much if someone could guide me or point me what I'm missing or doing incorrectly.
Below my code:
main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
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 MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Wrapper(),
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => const HomePage(),
'/login': (BuildContext context) => const LoginPage(),
},
);
}
}
wrapper.dart
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
//print(FirebaseAuth.instance.currentUser);
if(FirebaseAuth.instance.currentUser?.email == null){
return const LoginPage();
}
else{
return const HomePage();
}
}
}
login_page.dart
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
String get title => 'login';
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String userEmail = "";
UserModel? _currentUser;
bool loading = false;
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Email',
),
),
),
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Password',
),
)),
CustomWidgets.socialButtonRect('Login with Facebook',
facebookColor, FontAwesomeIcons.facebookF, onTap: () async {
await signInWithFacebook();
}),
Padding(
padding: const EdgeInsets.all(15.0),
child:
loading ? const CircularProgressIndicator() : Container(),
),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
),
onWillPop: () async {
final shouldPop = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Do you want to go exit?'),
actionsAlignment: MainAxisAlignment.spaceBetween,
actions: [
TextButton(
onPressed: () {
Navigator.pop(context, true);
},
child: const Text('Yes'),
),
TextButton(
onPressed: () {
Navigator.pop(context, false);
},
child: const Text('No'),
),
],
);
},
);
return shouldPop!;
},
);
}
Future<void> signInWithFacebook() async {
final LoginResult loginResult =
await FacebookAuth.instance.login(permissions: ['email']);
// Create a credential from the access token
final OAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(loginResult.accessToken!.token);
final userData = await FacebookAuth.instance.getUserData();
FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
var snapshot = await FirebaseFirestore.instance
.collection('users')
.where('Email', isEqualTo: userData['email'])
.get();
if (snapshot.size == 0) {
await FirebaseFirestore.instance
.collection('users')
.add({'Email': userData["email"], 'Name': userData['name']});
}
//userEmail = userData["email"];
UserModel user = UserModel.fromJson(userData);
_currentUser = user;
setState(() {
loading = true;
});
Navigator.pushNamed(context, '/home');
}
}
home_page.dart
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FirebaseAuth auth = FirebaseAuth.instance;
bool loading = false;
String name = "";
#override
Widget build(BuildContext context) {
getName();
return Scaffold(
backgroundColor: Colors.blue,
bottomNavigationBar: const BottomBar(),
body: SafeArea(
child: Column(
children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return matchTile();
}),
Center(
child: ElevatedButton(
onPressed: () async {
//Navigator.pop(context,true);
//Navigator.popUntil(context, ModalRoute.withName('/login'));
Navigator.of(context).pushReplacementNamed('/login');
await signOut();
},
child: Text(name),
style: TextButton.styleFrom(
primary: Colors.redAccent,
onSurface: Colors.red,
),
),
),
],
),
),
);
}
Future<void> signOut() async {
await FacebookAuth.i.logOut();
await FirebaseAuth.instance.signOut();
}
Future<void> getName() async {
User? user = auth.currentUser;
await user?.reload();
user = auth.currentUser;
String? temp = user?.email;
var snapshot = await FirebaseFirestore.instance
.collection('users')
.where('Email', isEqualTo: temp)
.limit(1)
.get()
.then((value) => value.docs[0]);
Map<String, dynamic> data = snapshot.data();
//print('the lenght : $data.length');
setState(() {
name = data['Name'];
});
}
}
For last these are the rules I have set in firebase
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
You probably are missing an await.
Without this await the user is not logged when accessing Firestore, as the code "just runs by".
await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);

Flutter Bloc is not emitting or triggering the event

I am building a mobile application using Flutter. I am using Bloc Flutter library. I am a beginner to Flutter BloC. Now, I am refactoring my login form using BLoC. But it seems that it is not dispatching the BLoC event when the login button is clicked.
I have the LoginBloc class with the following code:
class LoginBloc extends Bloc<LoginEvent, LoginState> {
LoginBloc() : super(LoginState.initial()) {
on<LoginEvent>((event, emit) {
// yield the state here. check the event and then process the event and yield the state based on the result.
if (event is Login) {
ApiService.post(ApiEndpoints.login, {
'email': event.email,
'password': event.password
}).then((response) => () {
if (response.statusCode == 200) {
// TODO: provide implementation
var responseJson = jsonDecode(response.body);
MeData meData = MeData.fromJson(responseJson['data']);
} else {
ApiError apiError = Utilities.parseApiError(response.body);
emit(LoginState(event.email, event.password, false, apiError));
}
}).onError((error, stackTrace) => () {
var apiError = ApiError();
apiError.setGenericErrorMessage("Something went wrong!");
emit(LoginState(event.email, event.password, false, apiError));
});
}
});
}
}
The is my login_event.dart file:
part of 'login_bloc.dart';
#immutable
abstract class LoginEvent extends Equatable {
const LoginEvent();
}
class Login extends LoginEvent {
final String email = "";
final String password = "";
const Login(email, password);
#override
List<Object?> get props => [
email,
password
];
}
This is my login_state.dart file:
part of 'login_bloc.dart';
class LoginState extends Equatable {
final String email;
final String password;
final bool isLoading;
final ApiError error;
const LoginState(this.email, this.password, this.isLoading, this.error);
static LoginState initial()
{
return LoginState("", "", false, ApiError());
}
#override
List<Object?> get props => [
email,
password,
isLoading,
error
];
}
This is my login.dart file (screen)
class LoginPage extends StatefulWidget {
final String title = 'Login';
#override
State<LoginPage> createState() => _LoginPage();
}
class _LoginPage extends State<LoginPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String? _email;
String? _password;
ApiError _apiError = ApiError();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title)
),
body: BlocBuilder<LoginBloc, LoginState>(
builder: (context, state) {
return Center(
child: Form(
key: _formKey,
child: Column(
children: [
GenericFormError(errorMessage: state.error.getGenericErrorMessage()),
Padding(
padding: const EdgeInsets.all(10),
child: TextFormField(
decoration: InputDecoration(
labelText: "Email",
border: const OutlineInputBorder(),
errorText: _apiError.getFieldError("email")
),
onChanged: (value) => setState(() {
_email = value;
}),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter email";
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.all(10),
child: TextFormField(
obscureText: true,
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
labelText: "Password",
border: const OutlineInputBorder(),
errorText: _apiError.getFieldError("password")
),
onChanged: (value) => setState(() {
_password = value;
}),
validator: (value) {
if (value == null || value.isEmpty) {
return "Please enter password";
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: () {
var isValid = _formKey.currentState!.validate();
if (isValid) {
context.read<LoginBloc>().add(Login(_email.toString(), _password.toString()));
}
},
child: const Text('Login'),
),
),
),
],
),
)
);
}
),
);
}
}
As you can see in the login form, when the button is clicked and form is valid, I am dispatching the BLoC event using this code.
context.read<LoginBloc>().add(Login(_email.toString(), _password.toString()));
But it is not triggering LoginBloc() at all. What is wrong with my code and how can I fix it? It is not throwing any errors either.
This is how I initialised the Bloc Provider:
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 MultiBlocProvider(
providers: [
BlocProvider(create: (BuildContext context) {
return LoginBloc();
})
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Shar Kya Mal'),
));
}
}
I think you haven't initialized the bloc provider for it yet. You can wrap whole widget with bloc provider and init LoginBloc or init on file app
Adding Bloc Provider in file app.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: Scaffold(
body: BlocProvider(
create: (context) => NameBloc(NameBlocRepository()),
child: HomeScreen(),
),
));
}
}
or this
init bloc service and add to bloc provider
_nameBloc = nameBloc(blocService: _blocService);
return MultiBlocProvider(
providers: [ BlocProvider.value( value: _userBloc, )], child: MaterialApp

Bloc State is yielded but UI is not updating

I'm having an issue using bloc. The bloc state is updating but it seems to be no effect on UI. I have reproduced the issue using an example app. The issue is removing an item from the list page does not change is color back to deselected state. Actually what happens is When you select an item on home page the cart button appears and on no items selected state it disappears. So when you selected an item on homepage the cart button appears. When you click on the button you're navigated to another page i.e. ListPage where you can see the list of all selected items and a remove button corresponding to every item. When you remove an item it gets removed from ListPage but still it's color is Red i.e. selected on HomePage. I have to click again to change the color although it was deselected i.e. (item.active = false) on removing it from the ListPage. Just the color stays the same, it's not getting updated. The code files are given below:
item_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/item_model.dart';
import 'package:demo/repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
part 'items_event.dart';
part 'items_state.dart';
class ItemsBloc extends Bloc<ItemsEvent, ItemsState> {
final ItemRepository itemRepository;
final CartBloc cartBloc;
ItemsBloc({
#required this.itemRepository,
#required this.cartBloc,
}) : super(ItemsLoading());
#override
Stream<ItemsState> mapEventToState(
ItemsEvent event,
) async* {
if (event is GetItems) {
yield* _mapGetItemsToState(event);
} else if (event is UpdateItem) {
yield* _mapUpdateItemToState(event);
}
}
Stream<ItemsState> _mapGetItemsToState(GetItems event) async* {
yield ItemsLoading();
try {
final _items = await itemRepository.loadItems();
yield ItemsLoaded(items: _items);
print('Yielded');
} catch (_) {
yield ItemsFailure();
}
}
Stream<ItemsState> _mapUpdateItemToState(UpdateItem event) async* {
try {
_updateRepositoryItems(event.item);
if (event.item.active) {
cartBloc.add(ShowItems());
} else {
cartBloc.add(RemoveItem(itemId: event.item.id));
}
yield* _mapGetItemsToState(GetItems());
} catch (_) {
yield ItemsFailure();
}
}
Item _updateRepositoryItems(Item item) => itemRepository.setItem = item;
}
cart_bloc.dart
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:demo/item_data_model.dart';
import 'package:demo/repository.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
part 'cart_event.dart';
part 'cart_state.dart';
class CartBloc extends Bloc<CartEvent, CartState> {
final ItemRepository itemRepository;
CartBloc({#required this.itemRepository}) : super(CartLoading());
#override
Stream<CartState> mapEventToState(
CartEvent event,
) async* {
if (event is ShowItems) {
yield* _mapShowItemsToState(event);
} else if (event is RemoveItem) {
yield* _mapRemoveItemToState(event);
}
}
Stream<CartState> _mapShowItemsToState(ShowItems event) async* {
try {
final _selectedItems = await itemRepository.getSelectedItems;
if (_selectedItems.isNotEmpty) {
final data = ItemData(items: _selectedItems);
yield CartLoaded(itemData: data);
} else {
yield CartLoading();
}
} catch (_) {
yield CartFailure();
}
}
Stream<CartState> _mapRemoveItemToState(RemoveItem event) async* {
try {
final _selectedSounds = await itemRepository.getSelectedItems;
if (_selectedSounds.isEmpty) {
yield CartLoading();
} else {
yield* _mapShowItemsToState(ShowItems());
}
} catch (_) {
yield CartFailure();
}
}
}
homepage.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/item_tile.dart';
import 'package:demo/list_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("Home Page"),
centerTitle: true,
),
body: Column(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: BlocBuilder<ItemsBloc, ItemsState>(
// ignore: missing_return
builder: (context, state) {
if (state is ItemsLoading) {
return Center(
child: CircularProgressIndicator(),
);
} else if (state is ItemsLoaded) {
print('Items Loaded');
return GridView.count(
crossAxisCount: 3,
crossAxisSpacing: 20.0,
mainAxisSpacing: 20.0,
childAspectRatio: 0.6,
children: [
for (var item in state.items) ItemTile(item: item),
],
);
} else if (state is ItemsFailure) {
return Center(
child: Text(
'Unexpected Failure, Cannot Load Items',
style: Theme.of(context).textTheme.headline6,
),
);
}
},
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: CartButton(),
),
],
),
),
);
}
}
class CartButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocBuilder<CartBloc, CartState>(
// ignore: missing_return
builder: (context, state) {
if (state is CartLoading) {
return SizedBox.shrink();
} else if (state is CartLoaded) {
print('Cart Loaded');
return TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) {
return ListPage(
items: state.itemData.items,
);
}),
);
},
child: Text('Go To Cart'),
);
} else if (state is CartFailure) {
return Center(
child: Text(
'Unexpected Failure, Cannot Load Cart',
style: Theme.of(context).textTheme.headline6,
),
);
}
},
);
}
}
item_tile.dart
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:demo/item_model.dart';
import 'package:flutter/material.dart';
class ItemTile extends StatefulWidget {
final Item item;
const ItemTile({Key key, this.item}) : super(key: key);
#override
_ItemTileState createState() => _ItemTileState();
}
class _ItemTileState extends State<ItemTile> {
bool active;
#override
void initState() {
super.initState();
active = widget.item.active;
}
void _onItemTap() {
setState(() {
active = !active;
});
getIt<ItemsBloc>()
.add(UpdateItem(item: widget.item.copyWith(active: active)));
}
#override
Widget build(BuildContext context) {
return ListTile(
title: Text(widget.item.title),
tileColor: active ? Colors.redAccent : Colors.blueAccent,
onTap: () {
_onItemTap();
},
);
}
}
item_page.dart
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:flutter/material.dart';
import 'item_model.dart';
class ListPage extends StatefulWidget {
final List<Item> items;
const ListPage({Key key, this.items}) : super(key: key);
#override
_PlayerScreenState createState() => _PlayerScreenState();
}
class _PlayerScreenState extends State<ListPage> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text(
'Cart List Page',
style: Theme.of(context).textTheme.headline6,
),
centerTitle: true,
),
body: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.separated(
itemCount: widget.items.length,
separatorBuilder: (BuildContext context, int index) =>
Divider(),
itemBuilder: (BuildContext context, int index) {
return ListTile(
trailing: IconButton(
icon: Icon(
Icons.close_rounded,
color: Colors.black,
),
onPressed: () {
getIt<ItemsBloc>().add(UpdateItem(
item: widget.items.elementAt(index).copyWith(
active: !widget.items.elementAt(index).active),
));
setState(() {
widget.items.removeAt(index);
});
if (widget.items.isEmpty) {
Navigator.pop(context);
}
},
),
title: Text(
widget.items.elementAt(index).title,
),
);
},
),
),
],
),
),
),
);
}
}
get_it.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/repository.dart';
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
Future<void> init() async {
// Bloc
getIt.registerLazySingleton<CartBloc>(() => CartBloc(itemRepository: getIt()));
getIt.registerFactory(() => ItemsBloc(
itemRepository: getIt(),
cartBloc: getIt(),
));
// Repository
getIt.registerSingleton<ItemRepository>(ItemRepository());
}
main.dart
import 'package:demo/bloc/cart/cart_bloc.dart';
import 'package:demo/bloc/items/items_bloc.dart';
import 'package:demo/get_it.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'get_it.dart' as di;
import 'package:flutter/material.dart';
import 'home_page.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
di.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => getIt<ItemsBloc>()..add(GetItems()),
),
BlocProvider(
create: (context) => getIt<CartBloc>(),
),
],
child: MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
),
);
}
}
Down Here are the images of the issue:
Ok, so based on the comments, that indicates that you're already in an ItemsLoaded state when removing the item. Since it's not a new state, then a rebuild is not triggered. So it's behaving as expected. Looks like you need to rethink how you do this, perhaps by yielding an ItemsUpdated state before then yielding the ItemsLoaded state.

FutureBuilder doesn't update when updating the database

I was working in a simple notes app in flutter, this is my code.
i just made this example to preview the problem
This is the home page that navigate to a create page, and uses a futureBuilder to display the data from the database
import 'package:flutter/material.dart';
import 'package:futurebuilder_test/db.dart';
import 'create.dart';
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final DatabaseHelper db = DatabaseHelper.instance;
Future futureData;
#override
void initState() {
futureData = _getData();
super.initState();
}
Future _getData() async{
return await db.queryAllRows('notes');
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () => _navigateToCreatePage(context),
),
body: FutureBuilder(
future: _getData(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, index) {
return ListTile(
title: Text(snapshot.data[index]['title']),
);
},
);
}
return Text('Empty');
},
),
);
}
}
void _navigateToCreatePage(context) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => CreatePage())
);
}
and this is the create page
import 'package:flutter/material.dart';
import 'package:futurebuilder_test/db.dart';
class CreatePage extends StatelessWidget {
final DatabaseHelper db = DatabaseHelper.instance;
final TextEditingController title = TextEditingController();
final TextEditingController description = TextEditingController();
void save(context) async {
await db.insert(
'notes', {'title': title.text, 'description': description.text}
);
Navigator.pop(context);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
TextField(
decoration: InputDecoration(hintText: 'Title'),
controller: title,
),
TextField(
decoration: InputDecoration(hintText: 'Title'),
controller: description,
),
RaisedButton(
child: Text('Save'),
onPressed: () => save(context),
)
],
),
),
);
}
}
when the user click the save button it adds a new note to the database and pop the page back to home screen It's supposed that the future builder update it's data after submitting new data to the database
but this is not happening. could someone help me.
You should dispose the previous page using this or handle everything with state management.