Closed seklum closed 5 years ago
Hi @seklum 👋
Thanks for opening an issue. Can you please provide an example app with the problem you're having? It's hard for me to give any suggestions without a concrete example. Thanks!
This code loads up your contacts to a list and the idea is to hold the state of which contacts are selected while you scroll through and tap names.
If you tap a name and then scroll so the item is rebuilt it shows that it is selected, proving the code is running without triggering the onTransition or BlocBuilder updating.
pubspec.yaml
dependencies:
contacts_service: ^0.2.1
permission_handler: ^2.2.0
bloc: ^0.9.5
flutter_bloc: ^0.7.1
equatable: ^0.2.2
main.dart
import 'dart:math';
import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:contacts_service/contacts_service.dart';
import 'package:test_app/bloc/ContactBloc.dart';
import 'package:test_app/bloc/contact_event.dart';
import 'package:test_app/bloc/contact_state.dart';
class SimpleBlocDelegate extends BlocDelegate {
@override
void onTransition(Transition transition) {
print(transition);
}
}
void main() {
BlocSupervisor().delegate = SimpleBlocDelegate();
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: PeopleScreen(),
);
}
}
class ContactItem {
ContactItem(this.cont, this.selected);
Contact cont;
bool selected = false;
}
class PeopleScreen extends StatefulWidget {
PeopleScreen({Key key}) : super(key: key);
@override
_PeopleScreenState createState() => _PeopleScreenState();
}
class _PeopleScreenState extends State<PeopleScreen> {
final ContactBloc _contactBloc = ContactBloc();
Widget getContacts() {
return BlocBuilder(
bloc: _contactBloc,
builder: (BuildContext context, ContactItemState state) {
if (state is Uninitialized) {
_contactBloc.dispatch(InitializeEvent());
return Text('Loading');
}
if (state is ContactItemLoaded) {
List<Widget> contactWidgets = List<Widget>();
state.posts
.forEach((cont) => contactWidgets.add(BlocProvider<ContactBloc>(
bloc: _contactBloc,
child: ContactWidget(cont),
)));
return ListView(
shrinkWrap: true,
padding: const EdgeInsets.all(20.0),
children: contactWidgets,
);
}
},
);
}
@override
void dispose() {
_contactBloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
initialIndex: 0,
child: Scaffold(
appBar: AppBar(
title: Text('Invite'),
bottom: new TabBar(
tabs: <Widget>[
new Tab(
text: "Contacts",
),
new Tab(
text: "Friends",
),
new Tab(
text: "Recents",
),
],
),
),
body: new TabBarView(
children: <Widget>[
getContacts(),
new Container(
color: Colors.deepOrangeAccent,
child: new Center(
child: new Text(
"Second",
// style: textStyle(),
),
),
),
new Container(
color: Colors.deepOrangeAccent,
child: new Center(
child: new Text(
"Third",
// style: textStyle(),
),
),
),
],
),
),
);
}
}
class ContactWidget extends StatefulWidget {
ContactWidget(this.contact, {Key key}) : super(key: key);
final ContactItem contact;
@override
_ContactWidgetState createState() => _ContactWidgetState();
}
class _ContactWidgetState extends State<ContactWidget> {
List<Color> colors = [
Colors.green,
Colors.amber,
Colors.blue,
Colors.amberAccent,
Colors.blueAccent,
Colors.deepOrange,
Colors.greenAccent,
Colors.orange,
Colors.purpleAccent,
Colors.yellow
];
@override
Widget build(BuildContext context) {
ContactBloc _contactBloc = BlocProvider.of<ContactBloc>(context);
Random random = Random();
Color color = colors[random.nextInt(colors.length)];
if (widget.contact.selected)
return Container(
decoration: BoxDecoration(
border: Border.all(width: 1.0, color: Colors.green),
),
child: ListTile(
leading: Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(
backgroundColor: color,
foregroundColor: Colors.white,
child: new Text(widget.contact.cont.displayName[0]),
),
),
title: Text(widget.contact.cont.displayName),
onTap: () {
_contactBloc.dispatch(SelectEvent(widget.contact.cont.displayName));
},
enabled: true,
),
);
return ListTile(
leading: Container(
margin: const EdgeInsets.only(right: 16.0),
child: CircleAvatar(
backgroundColor: color,
foregroundColor: Colors.white,
child: new Text(widget.contact.cont.displayName[0]),
),
),
title: Text(widget.contact.cont.displayName),
onTap: () {
_contactBloc.dispatch(SelectEvent(widget.contact.cont.displayName));
},
enabled: true,
);
}
}
contact_event.dart
import 'package:equatable/equatable.dart';
abstract class ContactEvent extends Equatable {}
class InitializeEvent extends ContactEvent {
@override
String toString() => 'Initialize Event';
}
class SelectEvent extends ContactEvent {
String displayName;
SelectEvent(this.displayName);
@override
String toString() => 'Selected: $displayName';
}
contact_state.dart
import 'package:equatable/equatable.dart';
import 'package:test_app/main.dart';
abstract class ContactItemState extends Equatable {
ContactItemState([List props = const []]) : super(props);
}
class Uninitialized extends ContactItemState {
@override
String toString() =>
'Uninitialized';
}
class ContactItemLoaded extends ContactItemState {
final List<ContactItem> posts;
ContactItemLoaded({
this.posts,
}) : super(posts);
ContactItemLoaded copyWith({
List<ContactItem> posts,
}) {
return ContactItemLoaded(
posts: posts ?? this.posts,
);
}
@override
String toString() =>
'ContactItemLoaded { contacts: ${posts.length} }';
}
ContactBloc.dart
import 'package:bloc/bloc.dart';
import 'package:contacts_service/contacts_service.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:test_app/bloc/contact_event.dart';
import 'package:test_app/bloc/contact_state.dart';
import 'package:test_app/main.dart';
class ContactBloc extends Bloc<ContactEvent, ContactItemState> {
@override
// TODO: implement initialState
ContactItemState get initialState => Uninitialized();
@override
Stream<ContactItemState> mapEventToState(
ContactItemState currentState,
ContactEvent event,
) async* {
if(event is SelectEvent && currentState is ContactItemLoaded) {
ContactItem item = currentState.posts.firstWhere((e) => e.cont.displayName == event.displayName);
item.selected = !item.selected;
yield currentState;
}
else if(currentState is Uninitialized){
yield ContactItemLoaded(posts: await getContacts());
}
else
yield ContactItemLoaded(posts: await getContacts());
}
Future<Iterable<Contact>> loadContacts() async {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.contacts);
if (permission != PermissionStatus.granted) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.contacts]);
assert(permissions[PermissionGroup.contacts] == PermissionStatus.granted);
}
Iterable<Contact> cont = await ContactsService.getContacts();
return cont;
}
Future<List<ContactItem>> getContacts() async {
List<ContactItem> list = List<ContactItem>();
Iterable<Contact> contacts = await loadContacts();
contacts.toList().forEach((Contact cont) {
if(cont.phones.length > 0)
list.add(ContactItem(cont, false));
});
list.sort((contA, contB) => contA.cont.displayName.compareTo(contB.cont.displayName));
return list;
}
}
@seklum thanks for the code! 👍 I'll have a look in the next few hours.
@seklum There were several problems with the code you sent me. I have corrected them and you can find a working version of this here.
The main problem was in your bloc you were mutating currentState
and yielding it. You cannot mutate your state; instead, you must always yield a new state otherwise bloc thinks nothing has changed and no transition will occur. Let me know if you have any other questions! 😄
Hello, I'm working on a firebase auth, when I try to login it pass successfully but in the console the transition show me that currentState uninitialized. this is my bloc`class AuthBloc extends Bloc<AuthEvent, AuthState> { final AuthRepository _authRepository = AuthRepository(auth: FirebaseAuth.instance, googleSignin: GoogleSignIn()); StreamSubscription _todosSubscription;
@override AuthState get initialState => Uninitialized();
@override
Stream
}
if (event is AuthChanged) {
if(event.user!=null){
//yield Unauthenticated();
//}else{
yield* _mapAuthChangedState(event);
}
}
if (event is LoggedOut) {
yield Unauthenticated();
_authRepository.logout();
}
}
Stream
Stream
@override
Future
I would like to have currentState as Authenticated after login. please help me
When using the bloc provider to give a bloc to a child widget any dispatch called on it does not show up in transitions or update the bloc builder in the parent widget.
It does function correctly in the background but I need it to automatically update the BlocBuilder in the parent widget when each event occurs.