Flutter Auth (BLoC pattern & rxDart) - flutter

I wanted to make applications with authorization on the BLoC partner, but I encountered an error:
The following NoSuchMethodError was thrown building AuhtScreen(dirty, state: _AuhtScreenState<dynamic>#00539):
The getter 'blocState' was called on null.
Receiver: null
Tried calling: blocState
It is called under the following circumstances:
In AuhtScreen
AuthBloc authBloc = BlocProvider.of(context).authBloc; (context = StatefulElement)
In BlocProvider
static BlocState of(BuildContext context) { (context = StatefulElement)
return (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider).blocState; (context = StatefulElement)(NULL)
}
I do not understand why it does not work, I do everything correctly, maybe I missed something or did not understand... Help solve the problem!
All code:
AuthBloc
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:mifity/models/auht_detail.dart';
import 'package:mifity/models/user.dart';
import 'package:mifity/screens/main_screen.dart';
import 'package:mifity/services/auth_service.dart';
import 'package:rxdart/rxdart.dart';
class AuthBloc {
AuthService authService;
BuildContext _context;
final currentUserSubject = BehaviorSubject<User>.seeded(null);
final emailSubject = BehaviorSubject<String>.seeded('');
final passwordSubject = BehaviorSubject<String>.seeded('');
final loadingSubject = BehaviorSubject<bool>.seeded(false);
final loginSubject = BehaviorSubject<Null>.seeded(null);
//sink
void Function(String) get emailChanged => emailSubject.sink.add;
void Function(String) get passwordChanged => passwordSubject.sink.add;
void Function(BuildContext) get submitLogin => (context) {
this.setContext(context);
loginSubject.add(null);
};
//stream
Stream<User> get currentUser => currentUserSubject.stream;
Stream<String> get emailStream => emailSubject.stream;
Stream<String> get passwordStream => passwordSubject.stream;
Stream<bool> get loading => loadingSubject.stream;
AuthBloc({this.authService}) {
Stream<AuhtDetail> auhtDetailStream = Observable.combineLatest2(
emailStream, passwordStream, (email, password) {
return AuhtDetail(email: email, password: password);
});
Stream<User> loggedIn = Observable(loginSubject.stream)
.withLatestFrom(auhtDetailStream, (_, auhtDetail) {
return auhtDetail;
}).flatMap((auhtDetail) {
return Observable.fromFuture(authService.loginUser(auhtDetail))
.doOnListen(() {
loadingSubject.add(true);
}).doOnDone(() {
loadingSubject.add(false);
});
});
loggedIn.listen((User user) {
currentUserSubject.add(user);
Navigator.push(
_context,
new MaterialPageRoute(builder: (context) => MainScreen()),
);
}, onError: (error) {
Scaffold.of(_context).showSnackBar(new SnackBar(
content: new Text("Username or password incorrect"),
));
});
}
setContext(BuildContext context) {
_context = context;
}
close() {
emailSubject.close();
passwordSubject.close();
loadingSubject.close();
loginSubject.close();
}
}
BlocProvider
import 'package:flutter/material.dart';
import 'package:mifity/blocs/auth_bloc.dart';
import 'package:mifity/services/auth_service.dart';
class BlocProvider extends InheritedWidget {
final blocState = new BlocState(
authBloc: AuthBloc(authService: AuthService()),
);
BlocProvider({Key key, Widget child}) : super(key: key, child: child);
bool updateShouldNotify(_) => true;
static BlocState of(BuildContext context) {
return (context.inheritFromWidgetOfExactType(BlocProvider) as BlocProvider)
.blocState;
}
}
class BlocState {
final AuthBloc authBloc;
BlocState({this.authBloc});
}
AuthService
import 'dart:async';
import 'package:mifity/models/auht_detail.dart';
import 'package:mifity/models/error.dart';
import 'package:mifity/models/user.dart';
class AuthService {
Future<User> loginUser(AuhtDetail detail) async {
await Future.delayed(Duration(seconds: 1)); //simulate network delay
if (detail.email == 'johndoe#acme.com' && detail.password == '1234') {
return User(
id: 1,
name: 'John Doe',
email: 'johndoe#acme.com',
age: 26,
profilePic: 'john_doe.png');
} else {
throw ClientError(message: 'login details incorrect.');
}
}
}
Validator:
class Validator {
String validateEmail(String value) {
if (value.isEmpty) return 'Email Should not be empty';
final RegExp emailRegEx = new RegExp(r'^\w+#[a-zA-Z_]+?\.[a-zA-Z]{2,3}$');
if (!emailRegEx.hasMatch(value)) return 'Your Email is invalid';
return null;
}
String validatePassword(String value) {
if (value.length < 4) return 'Password should be four characters or more';
return null;
}
}
AuhtScreen
import 'package:flutter/material.dart';
import 'package:mifity/blocs/auth_bloc.dart';
import 'package:mifity/blocs/bloc_provider.dart';
import 'package:mifity/helpers/validators.dart';
class AuhtScreen extends StatefulWidget {
#override
_AuhtScreenState createState() => _AuhtScreenState();
}
class _AuhtScreenState<StateClass> extends State<AuhtScreen> {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
Validator validator = new Validator();
final formKey = GlobalKey<FormState>();
DecorationImage backgroundImage = new DecorationImage(
image: new ExactAssetImage('assets/images/bg_image.jpg'),
fit: BoxFit.cover,
);
#override
Widget build(BuildContext context) {
AuthBloc authBloc = BlocProvider.of(context).authBloc;
final Size screenSize = MediaQuery.of(context).size;
return Scaffold(
appBar: AppBar(
title: Text('Login'),
),
body: Builder(builder: (context) {
return SingleChildScrollView(
child: Container(
height: screenSize.height - AppBar().preferredSize.height,
padding: EdgeInsets.all(10.0),
alignment: Alignment.center,
decoration: BoxDecoration(
image: (backgroundImage != null) ? backgroundImage : null),
child: Center(
child: Form(
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
style: TextStyle(color: Colors.white),
controller: emailController,
decoration: InputDecoration(
labelText: 'email',
labelStyle: TextStyle(color: Colors.grey)),
validator: validator.validateEmail,
),
TextFormField(
style: TextStyle(color: Colors.white),
controller: passwordController,
decoration: InputDecoration(
labelText: 'password',
labelStyle: TextStyle(color: Colors.grey)),
obscureText: true,
validator: validator.validatePassword,
),
SizedBox(
height: 20.0,
),
StreamBuilder<bool>(
initialData: false,
stream: authBloc.loading,
builder: (context, loadingSnapshot) {
return SizedBox(
width: double.infinity,
child: RaisedButton(
color: Colors.deepOrange,
textColor: Colors.white,
child: Text((loadingSnapshot.data)
? 'Login ...'
: 'Login'),
onPressed: () {
_submit(context, authBloc);
},
),
);
},
),
],
),
),
),
),
);
}));
}
_submit(context, AuthBloc authBloc) {
authBloc.emailChanged(emailController.text);
authBloc.passwordChanged(passwordController.text);
if (formKey.currentState.validate()) {
authBloc.submitLogin(context);
}
}
}

I'm an idiot!)
MyApp:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider(
child: MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new AuhtScreen(),
));
}
}

Related

Flutter Dynamic Searching appBar

I'm trying to create a dynamic search in the appBar, but unfortunately I'm not succeeding and I have an error on this line.
results = _listPlaces.where((places) => places["place"].toLowerCase().contains(enteredKeyword.toLowerCase())
Full code:
import 'package:favspot/src/views/places_card.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../../services/places.dart';
import 'dart:async';
class SeachPlaces extends StatefulWidget {
#override
State<SeachPlaces> createState() => _SeachPlacesState();
}
class _SeachPlacesState extends State<SeachPlaces> {
List<Object> _listPlaces = [];
List<Object> _searchSearch = [];
#override
initState(){
_searchSearch = _listPlaces;
super.initState();
}
void _runFilter(String enteredKeyword) {
List<Object> results = [];
if (enteredKeyword.isEmpty) {
results = _listPlaces;
} else {
results = _listPlaces.where((places) => places["place"].toLowerCase().contains(enteredKeyword.toLowerCase())
).toList();
setState((){
_searchSearch = results;
});
}
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
getPlacesList();
}
TextEditingController? _textEditngController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
width: double.infinity,
height: 40,
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(30)),
child: Center(
child:TextField(
onChanged: (value) => _runFilter(value),
controller: _textEditngController,
decoration: InputDecoration(
contentPadding: EdgeInsets.all(5),
prefixIcon: Icon(Icons.search),
suffix: IconButton(
icon: Icon(Icons.clear),
onPressed: null,
),
hintText: 'Searching...',
border: InputBorder.none,
),
),
),
),
),
body: SafeArea(
child: ListView.builder(
itemCount: _searchSearch.length,
itemBuilder: (context, index) {
return PlacesCard(_searchSearch[index] as Places);
},
)
),
);
}
Future getPlacesList() async {
var data = await FirebaseFirestore.instance
.collection('places')
.orderBy('city', descending: false)
.get();
setState(() {
_listPlaces = List.from(data.docs.map((doc) => Places.fromSnapshot(doc)));
});
}
}
Here is de class Places
class Places {
String? place;
String? city;
String? status;
Places();
Map<String, dynamic> toJson() => {
'city' : city,
'status' : status,
};
Places.fromSnapshot(snapshot) :
place = snapshot.id,
city = snapshot.data()['city'],
status = snapshot.data()['status'];
}
Consider to use List<Places> _listPlaces instead of List<Object> _listPlaces, so you can access properties like this:
results = _listPlaces.where((places) => places.place != null && places.place!.toLowerCase().contains(enteredKeyword.toLowerCase())
).toList();

About ChangeNotifier Provider

I am managing state using provider but ChangeNotifierProvider does not change the value of variable.
I want to show progress indicator when user is registering. But ChangeNotifierProvider does not provide me update value rather it always return me _isLoading = false;
My Code:
AuthServices.dart
class AuthServices with ChangeNotifier {
bool _isLoading = false;
bool get loading => _isLoading;
///Register User
Future registerUser(
{#required String email, #required String password}) async {
try {
_isLoading = true;
notifyListeners();
await http.post(
BackEndConfigs.baseUrl +
BackEndConfigs.version +
BackEndConfigs.auth +
EndPoints.register,
body: {
"email": email,
"password": password
}).then((http.Response response) {
_isLoading = false;
notifyListeners();
return RegisterUser.fromJson(json.decode(response.body));
});
} catch (e) {
print(e);
}
}
}
RegisterScreen.dart
class RegisterScreen extends StatefulWidget {
#override
_RegisterScreenState createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen> {
AuthServices _authServices = AuthServices();
ProgressDialog pr;
#override
Widget build(BuildContext context) {
pr = ProgressDialog(context, isDismissible: true);
// print(status.loading);
return Scaffold(
appBar:
customAppBar(context, title: 'Register Yourself', onPressed: () {}),
body: _getUI(context),
);
}
Widget _getUI(BuildContext context) {
return LoadingOverlay(
isLoading: false,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
VerticalSpace(10.0),
GreyBoldText('Account Information'),
VerticalSpace(20.0),
IconsButtonRow([
Icon(
FontAwesomeIcons.linkedinIn,
color: Color(0xff0e76a8),
),
Icon(
FontAwesomeIcons.facebook,
color: Color(0xff3b5998),
),
Icon(
FontAwesomeIcons.google,
color: Color(0xff4285F4),
),
]),
VerticalSpace(10.0),
GreyNormalText('or sign up with Email'),
VerticalSpace(10.0),
BlackBoldText("Email"),
AppTextField(
label: 'Email',
),
BlackBoldText("Password"),
AppTextField(
label: 'Password',
),
BlackBoldText("Confirm Password"),
AppTextField(
label: 'Confirm Password',
),
VerticalSpace(20.0),
ChangeNotifierProvider(
create: (_) => AuthServices(),
child: Consumer(
builder: (context, AuthServices user, _) {
return Text(user.loading.toString());
},
),
),
AppButton(
buttonText: 'Next',
onPressed: () async {
// await pr.show();
_registerNewUser();
}),
VerticalSpace(30.0),
ToggleView(ToggleViewStatus.SignUpScreen),
VerticalSpace(20.0),
],
),
),
),
);
}
_registerNewUser() async {
_authServices.registerUser(
email: 'sdjfkldsdf#ssdfdsddfdgsffd.com', password: 'sldjsdfkls');
}
}
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
scaffoldBackgroundColor: Colors.white,
hintColor: FrontEndConfigs.hintColor,
cursorColor: FrontEndConfigs.hintColor,
fontFamily: 'Gilory'),
home: RegisterScreen(),
);
}
}
You created two different instances of AuthServices.
One at the start of your State class and one using the ChangeNotifierProvider.
When calling _registerNewUser you use the AuthServices created in your state class not the provided one.
When you call registerUser on the first AuthServices, the value does not change for the second AuthServices provided by the ChangeNotifierProvider down in the Widget tree.
Try deleting the AuthServices instance created by the state class and move your ChangeNotifierProvider up the widget tree so all your functions share the same instance of AuthServices.

Handle callback response from Post method with bloc

I am calling login api on button click, I am able to get response from server but on clicking on button ,i am not able to received response.How can i receive the response and handle it .I am using BLoC pattern for this. Here is the code,
import 'package:flutter/material.dart';
import '../blocs/bloc.dart';
import '../blocs/provider.dart';
import '../models/login_response.dart';
class LoginScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Provider(
child: new Scaffold(
body: Container(
child: LoginForm(),
),
),
);
}
}
class LoginForm extends StatefulWidget {
// since its a stateful widget we need to create state for it.
const LoginForm({Key key}) : super(key: key);
#override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
#override
Widget build(BuildContext context) {
return Form(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 50),
),
// Start creating widget here.
emailField(),
passwordField(),
Container(margin: EdgeInsets.only(top: 25.0)),
submitButton()
],
),
);
}
Widget emailField() {
return StreamBuilder(
stream: bloc.email,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'you#example.com',
labelText: 'Email Address',
errorText: snapshot.error
),
);
}
);
}
Widget passwordField() {
return StreamBuilder(
stream: bloc.password,
builder: (context, snapshot) {
return TextField(
onChanged: bloc.changePassword,
obscureText: true,
decoration: InputDecoration(
labelText: 'Please enter your password',
hintText: 'Password',
errorText: snapshot.error
),
);
},
);
}
Widget submitButton() {
return StreamBuilder(
stream: bloc.submitValid,
builder: (context, snapshot) {
return RaisedButton(
onPressed:() => showWidgetForNetworkCall(context),
// onPressed: () {
// // Do submit button action.
// showWidgetForNetworkCall(context);
// // callLoginApi();
// },
child: const Text('Login'),
textColor: Colors.white,
color: Colors.blueAccent,
);
},
);
}
// Loading Widget
Widget _buildLoadingWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Loading data from API...", textDirection: TextDirection.ltr), CircularProgressIndicator()
],
),
);
}
// // Error Widget
Widget _buildErrorWidget(String error) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Loading error data from API...", textDirection: TextDirection.ltr), CircularProgressIndicator()
],
),
);
}
// show server data
showServerData() {
print(" Servr >>>>>> Data : ");
}
Widget showWidgetForNetworkCall(BuildContext context) {
bloc.loginSubmit();
return StreamBuilder(
stream: bloc.loginSubject.stream,
builder: (context, AsyncSnapshot<LoginResponse>snapshot){
if (snapshot.hasData) {
return showServerData();
} else if (snapshot.hasError) {
return _buildErrorWidget(snapshot.error);
} else {
return _buildLoadingWidget();
}
},
);
}
}
This is my login_screen.dart. And my bloc class for api call is:
postData() async {
LoginResponse response = await _repository.postData(_loginResource);
_subject.sink.add(response);
}
code of bloc
import 'dart:async';
import 'package:rxdart/rxdart.dart';
import 'validators.dart';
import '../models/login_response.dart';
import '../repository/login_repository.dart';
import '../resources/login_resource.dart';
class Bloc extends Object with Validators {
final LoginRepository _repository = LoginRepository();
final BehaviorSubject<LoginResponse> _subject =
BehaviorSubject<LoginResponse>();
LoginResource _loginResource = LoginResource();
final _email = BehaviorSubject<String>(); // Declaring variable as private
final _password = BehaviorSubject<String>(); // Declaring variable as private
// Add data to stream (Its like setter)
Stream<String> get email => _email.stream.transform(validateEmail);
Stream<String> get password =>
_password.stream.transform(validatePassword);
Stream<bool> get submitValid => Observable.combineLatest2(email, password, (e, p) => true);
// Change data. For retrieveing email value.
Function(String) get changeEmail => _email.sink.add;
Function(String) get changePassword => _password.sink.add;
loginSubmit() {
_loginResource.email = "bar1";
_loginResource.password = "bar2";
postData();
}
postData() async {
LoginResponse response = await _repository.postData(_loginResource);
_subject.sink.add(response);
}
dispose() {
_email.close();
_password.close();
_subject.close();
}
BehaviorSubject<LoginResponse> get loginSubject => _subject;
}
final bloc = Bloc();

DateFormField call save from form

the code from this question:
Receive Response from pop navigator in Flutter
with added DateTime picker form field.
If we add DateFormField like this:
maind.dart
import 'package:flutter/material.dart';
import 'package:date_field/date_field.dart';
import 'answer.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: ShowData(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
final myController = TextEditingController();
Data stateData = Data();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Scaffold(
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DateFormField(
initialDatePickerMode: DatePickerMode.day,
enabled: true,
key:_formKey,
onSaved: (DateTime value)
{
stateData.datefield = value;
},
validator: (DateTime value){
return stateData.datefield != null ? null : 'enter date';
},
firstDate: DateTime.now().subtract(Duration(days: 180)),
lastDate: DateTime.now().add(Duration(days: 365)),
),
TextFormField(
controller: myController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value){
stateData.load = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
// Validate returns true if the form is valid, or false
// otherwise.
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
// If the form is valid, display a Snackbar.
Navigator.pop(context,stateData);
// Scaffold.of(context)
// .showSnackBar(SnackBar(content: Text(myController.text)));
// myController.text = 'look at me';
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
class Data {
String load;
DateTime datefield;
}
when pop happens there is exception that the validator was called on null.
and "The following assertion was thrown while finalizing the widget tree:
Multiple widgets used the same GlobalKey."
if the key field is not used, then the validator is not called upon.
why?
how do you use DateFormField? from package
date_field: "^0.1.2"
adding answer.dart
import 'package:flutter/material.dart';
import 'main.dart';
class ShowData extends StatefulWidget {
#override
_ShowDataState createState() => _ShowDataState();
}
class _ShowDataState extends State<ShowData> {
String data = 'start';
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () async {
final holder = await getFormData(context);
setState(() {
data = holder.load;
});
},
elevation: 4,
),
body:Text(data,style: TextStyle(fontSize: 80),));
}
Future<Data> getFormData(BuildContext context) async {
final answer = await Navigator.push(context,MaterialPageRoute(builder: (context)=>MyCustomForm()));
return (Future.value(answer));
}
}
You can copy paste run full code below
Step 1: remove key:_formKey
DateFormField(
initialDatePickerMode: DatePickerMode.day,
enabled: true,
//key:_formKey,
Step 2: validator use value != null not stateData.datefield != null
validator: (DateTime value) {
//return stateData.datefield != null ? null : 'enter date';
return value != null ? null : 'enter date';
},
working demo
full code
import 'package:flutter/material.dart';
import 'package:date_field/date_field.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: Text(appTitle),
),
body: ShowData(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
final myController = TextEditingController();
Data stateData = Data();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Scaffold(
body: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
DateFormField(
initialDatePickerMode: DatePickerMode.day,
enabled: true,
//key:_formKey,
onSaved: (DateTime value) {
stateData.datefield = value;
},
validator: (DateTime value) {
//return stateData.datefield != null ? null : 'enter date';
return value != null ? null : 'enter date';
},
firstDate: DateTime.now().subtract(Duration(days: 180)),
lastDate: DateTime.now().add(Duration(days: 365)),
),
TextFormField(
controller: myController,
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
onSaved: (value) {
stateData.load = value;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: RaisedButton(
onPressed: () {
// Validate returns true if the form is valid, or false
// otherwise.
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
// If the form is valid, display a Snackbar.
Navigator.pop(context, stateData);
// Scaffold.of(context)
// .showSnackBar(SnackBar(content: Text(myController.text)));
// myController.text = 'look at me';
}
},
child: Text('Submit'),
),
),
],
),
),
);
}
}
class Data {
String load;
DateTime datefield;
}
class ShowData extends StatefulWidget {
#override
_ShowDataState createState() => _ShowDataState();
}
class _ShowDataState extends State<ShowData> {
String data = 'start';
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
final holder = await getFormData(context);
print(holder.datefield);
setState(() {
data = holder.load;
});
},
elevation: 4,
),
body: Text(
data,
style: TextStyle(fontSize: 80),
));
}
Future<Data> getFormData(BuildContext context) async {
final answer = await Navigator.push(
context, MaterialPageRoute(builder: (context) => MyCustomForm()));
return (Future.value(answer));
}
}
This is now fixed with the version 0.2.0 of the package!

Failed assertion: line 4774 pos 14: '_dependents.isEmpty': is not true

I am new to Flutter and I am working on a chat app, and whenever i choose an user i should be able to talk to him in a private way, that's what I am doing, whenever i click someone I try to move to this Chat Screen, and then I am getting this error (see title).
But when I'm pressing the back button and try again it works and shows the chat like it should, this is really confusing me, and if someone have an idea where it comes from, it would be awesome.
Here's my chat.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:social/responsive/size_config.dart';
var _firestore = Firestore.instance;
FirebaseUser loggedInUser;
String groupChatId;
class Chat extends StatelessWidget {
static const String id = 'chat_screen';
final String peerEmail;
Chat({Key key, #required this.peerEmail}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'CHAT',
style: TextStyle(fontWeight: FontWeight.bold),
),
centerTitle: true,
),
body: ChatScreen(peerEmail: peerEmail));
}
}
class ChatScreen extends StatefulWidget {
final String peerEmail;
ChatScreen({this.peerEmail});
#override
_ChatScreenState createState() => _ChatScreenState(peerEmail: peerEmail);
}
class _ChatScreenState extends State<ChatScreen> {
final _auth = FirebaseAuth.instance;
final messageTextController = TextEditingController();
String peerEmail;
String messageText;
_ChatScreenState({this.peerEmail});
#override
void initState() {
super.initState();
getCurrentUser();
String email = loggedInUser.email;
getGroupId(email);
}
void getGroupId(String email) {
if (peerEmail.hashCode <= email.hashCode) {
setState(() {
groupChatId = '$peerEmail-$email';
});
} else {
setState(() {
groupChatId = '$email-$peerEmail';
});
}
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser();
if (user != null) {
loggedInUser = user;
setState(() {});
}
} catch (e) {
print(e);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: <Widget>[
MessageStream(),
Container(
decoration: BoxDecoration(color: Colors.red,borderRadius: BorderRadius.circular(10)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
controller: messageTextController,
onChanged: (value) {
//Do something with the user input.
messageText = value;
},
),
),
FlatButton(
onPressed: () {
//Implement send functionality.
messageTextController.clear();
print(messageText);
print(loggedInUser.email);
_firestore.collection('messages')
.document(groupChatId)
.collection(groupChatId).add({
'content': messageText,
'emailFrom': loggedInUser.email,
'emailTo': peerEmail,
});
},
child: Text(
'Send',
),
),
],
),
),
],
)));
}
}
class MessageStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _firestore
.collection('messages')
.document(groupChatId)
.collection(groupChatId)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.black),
),
);
} else {
final messages = snapshot.data.documents;
List<MessageDisplay> messageList = [];
for (var msg in messages) {
final message = msg.data['content'];
final emailTo = msg.data['emailTo'];
final emailFrom = msg.data['emailFrom'];
final messageDisplay = MessageDisplay(
message: message,
emailFrom: emailFrom,
emailTo: emailTo,
);
messageList.add(messageDisplay);
}
return Expanded(
child: ListView(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
children: messageList != null ? messageList:CircularProgressIndicator(),
),
);
} //
},
);
}
}
class MessageDisplay extends StatelessWidget {
MessageDisplay({this.message, this.emailFrom, this.emailTo});
final String message;
final String emailFrom;
final String emailTo;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(SizeConfig.safeBlockVertical * 3),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
message != null
? Text(
message,
style: TextStyle(
fontSize: SizeConfig.safeBlockVertical * 15,
color: Colors.black54,
),
)
: CircularProgressIndicator(),
emailFrom != null
? Text(
emailFrom,
)
: CircularProgressIndicator(),
],
),
);
}
}
Thanks for reading.
The most likely cause for this type of error is the new screen you are navigating to is trying to access information from the previous screen which it has no access to or has not inherited and therefore doesn't have the correct build context when trying to build causing this error.