Unhandled Exception: NoSuchMethodError: The method 'next' was called on null - flutter

I have 4 classes SignUp , Auth, PageOne and InWidget (inherited widget). In the classe signUpState i have a swiper that i can control using a controller.
SignUp
class SignUp extends StatefulWidget {
static const String id = 'history_page';
#override
SignUpState createState() => SignUpState();
goto(bool x) => createState().goto(x);
}
SignUpState
class SignUpState extends State<SignUp> {
SwiperController _swOneCtrl;
#override
void initState() {
_swOneCtrl = new SwiperController();
super.initState();
}
goto(bool anim){
_swOneCtrl.next(animation: anim);
print("goto fired");
}
}
Auth
class Auth extends StatelessWidget {
SignUp s = SignUp();
verifyPhoneNumber() {
s.goto(true);
}
}
PageOne
class PageOneState extends State<PageOne> {
#override
Widget build(BuildContext context) {
final MyInheritedWidgetState state = MyInheritedWidget.of(context);
return RaisedButton(
color: Colors.blueGrey,
disabledColor: Colors.grey[100],
textColor: Colors.white,
elevation: 0,
onPressed: !phonebtn
? null
: () {
final MyInheritedWidgetState state =
MyInheritedWidget.of(context);
state.verifyPhoneNumber();
},
child: Text("CONTINUER"),
),
);
}
}
The thing is i want to call verifyPhoneNumber() from auth that will call the goto() method from pageone using inwidget as intermediary but i'm getting this error :
Unhandled Exception: NoSuchMethodError: The method 'next' was called on null.
do you know why ?

Try to initialize at the time of declaration.
class SignUpState extends State<SignUp> {
SwiperController _swOneCtrl = new SwiperController();
#override
void initState() {
super.initState();
}
goto(bool anim){
_swOneCtrl.next(animation: anim);
print("goto fired");
}
}
Respond me if it works.

initState() is a method that is called once when the stateful widget is inserted in the widget tree.
We generally override this method if we need to do some sort of initialization work like registering a listener because unlike build() this method is called once.
As I think you are declaring Swipe controller in your SignUPState class.
class SignUpState extends State<SignUp> {
SwiperController _swOneCtrl;
#override
void initState() {
_swOneCtrl = new SwiperController();
super.initState();
}
goto(bool anim){
_swOneCtrl.next(animation: anim);
print("goto fired");
}
}
But you have initialized it in initState(). the problem is because you are not inserting your SignUp widget in widget tree so your swipe controller is not initializing and become null. So when you are calling the next method to null it is showing error.
As Solution first insert your Sign up widget in your Widget tree.
if my solution helped you. Please rate me.

Related

Simple Bloc Pattern and Network Connection check

Thank you for checking out my question :). I appreciate it!
What I am trying to do
I am trying to check if the user has an internet connection or not. For this, I use the bloc pattern. I am just starting out and I don't know what to do with these errors.
What the error messages are
The relevant error-causing widget was:
TestScreen
Testscreen:file//filepath (etc,etc)
Another exception was thrown: Each child must be laid out exactly once.
Another exception was thrown: Updated layout information required for RenderErrorBox NEEDS-LAYOUT NEEDS-PAINT to calculate semantics.
Another exception was thrown: Bad state: Future already completed.
I am new and these errors are overwhelming. These errors tell me nothing. I hope you can help me out!
Code
Homescreen
class HomeScreen extends StatelessWidget {
const HomeScreen({Key? key}) : super(key: key);
static String routeName = '/home';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Homepage'),
),
body: BlocProvider(
create: (context) => NetworkBloc()..add(ListenConnection()),
child: TestScreen(),
));
}
}
class TestScreen extends StatelessWidget {
const TestScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: BlocBuilder<NetworkBloc, NetworkState>(
builder: (context, state) {
if (state is ConnectionFailure) return Text("No Internet Connection");
if (state is ConnectionSuccess)
return Text("You're Connected to Internet");
else
return Text("");
},
),
);
}
}
Network Block
class NetworkBloc extends Bloc<NetworkEvent, NetworkState> {
NetworkBloc() : super(ConnectionInitial());
late StreamSubscription _subscription;
#override
Stream<NetworkState> mapEventToState(NetworkEvent event) async* {
if (event is ListenConnection) {
_subscription = DataConnectionChecker().onStatusChange.listen((status) {
add(ConnectionChanged(status == DataConnectionStatus.disconnected
? ConnectionFailure()
: ConnectionSuccess()));
});
}
if (event is ConnectionChanged) yield event.connection;
}
#override
Future<void> close() {
_subscription.cancel();
return super.close();
}
}
Network Event
abstract class NetworkEvent {}
class ListenConnection extends NetworkEvent {}
class ConnectionChanged extends NetworkEvent {
NetworkState connection;
ConnectionChanged(this.connection);
}
Network State
abstract class NetworkState {}
class ConnectionInitial extends NetworkState {}
class ConnectionSuccess extends NetworkState {}
class ConnectionFailure extends NetworkState {}
I hope you can help me out. Thank you very much!
There are a lot of issues with your code so I'm not sure if I got all of them.
You should not call .add() if you don't have registered event handler
//This is how you should register an event handler
NetworkBloc() : super(ConnectionInitial()) {
on<ListenConnection>((event, emit) {
// TODO: implement event handler
});
}
You are overriding a function mapEventToState(), when it doesn't override any of inherited methods
//#override NO NEED OF OVERRIDE HERE
Stream<NetworkState> mapEventToState(NetworkEvent event) async* {
if (event is ListenConnection) {
_subscription = DataConnectionChecker().onStatusChange.listen((status) {
add(ConnectionChanged(status == DataConnectionStatus.disconnected
? ConnectionFailure()
: ConnectionSuccess()));
});
}
if (event is ConnectionChanged) yield event.connection;
}
And I'm not sure if this is intended, but your mapEventToState() also never gets called and without any emit() methods your UI will never rebuild.

Flutter getx controller get the current page context

I would like to use context to show a custom dialog from cool alert in getxcontroller method.
I have created the following controller
class HomePageController extends GetxController {
#override
void onInit() {
super.onInit();
getData();
}
void getData(){
//perform http request here
//show cool alert
CoolAlert.show(
context: context, //here needs the build context
type: CoolAlertType.success
);
}
}
Am using this controller in my stateless widget like
class HomePage extends StatelessWidget {
HomePage({ Key? key }) : super(key: key);
final _c = Get.find<HomePageController>();
#override
Widget build(BuildContext context) {
return Container(
);
}
}
How can i get the current homepage BuildContext in the controller inorder to show the cool alert.
If you want to show a dialog or snackbar what need context as a required agument. You can use Get.dialog() and Get.snackbar, there function work same as showDialog and showSnackbar but *without* context or scaffod
you can add the context to the construct function:
#override
Widget build(BuildContext context) {
Get.put(HomePageController(context: context));
return Container();
}
and for the HomePageController:
note: you need to wrap the function with Future.delayed(Duration.zero)
otherwise it will throw an error
class HomePageController extends GetxController {
late BuildContext context;
HomePageController({required this.context});
void getData(){
Future.delayed(Duration.zero,(){
CoolAlert.show(
context: context, //here needs the build context
type: CoolAlertType.success
);
});
}
...
}
You Need to Initialize the controller on the homepage like following
class HomePage extends StatelessWidget {
HomePage({ Key? key }) : super(key: key);
final _c = Get.put(HomePageController())..getData(context);
#override
Widget build(BuildContext context) {
return Container(
);
}
}
This will call getData Function and remove the onInit Function and Pass Buildcontext context parameter in getData Function.

What is the right way to implement a callback/listener pattern?

Sorry if this is a novice question. I have the following repo file:
class TwitterRepo {
final TwitterRepoCallback _callback;
TwitterRepo(this._callback){
// do stuff
}
}
abstract class TwitterRepoCallback{
void onEvent();
}
In my UI file I have the following:
class TweetList extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _TweetListState();
}
}
class _TweetListState extends State<TweetList> implements TwitterRepoCallback {
final TwitterRepo _twitterRepo = TwitterRepo(this);
// other stuff like initState, build and onEvent
}
There is an error on
final TwitterRepo _twitterRepo = TwitterRepo(this);
where I use "this", Invalid reference to 'this' expression.
I'm at a loss on how to pass in my callback to receive events.
Try this.
class ParentPageState extends State<ParentPage> implement Callback{
...
#override
void callback(){
...
}
#override
void callback1(String str){
....
}
#override
Widget build(BuildContext context){
return Scaffold(
body : Container(
child : ChildPage(callback : this.callback, callback1 : this.callback1)
)
);
}
}
And ChildPage
import .....
//Main Point
typedef Callback = void Function();
typedef Callback1 = void Function(String str);
class ChildPage extends StatelessWidget{
final Callback _callback;
final Callback1 _callback1;
ChildPage({Callback callback, Callback1 callback1}): _callback : callback, _callback1 : callback1;
.....
#override
Widget build(BuildContext context){
return Container(
child : InkWell(
onPressed : (){
this._callback();
this._callback1("test");
},
child : ....
)
);
}
This is may have issue. The main point is "typedef"
I probably wouldn't use callbacks for this type of need. Instead I'd use some kind of InheritedWidget like system to grab data and propagate it down the widget tree. I know you just started, but a great tool is the Provider package. To do what you're trying to do here it'd look something like this:
class TwitterRepo extends ChangeNotifier{
//construct Repo
TwitterRepo(){
_setupNetworkListener();
}
List<Data> data = [];
//set up the way to listen to and get data here then add it to your list,
//finally notify your listeners of the data changes
_setupNetworkListener()async{
var _data = await gettingInfo();
data.addAll(_data);
notifyListeners();
}
}
class TwitterRepoUI extends StatefulWidget {
#override
_TwitterRepoUIState createState() => _TwitterRepoUIState();
}
class _TwitterRepoUIState extends State<TwitterRepoUI> {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<TwitterRepo>(
builder: (context)=> TwitterRepo(),
child: Consumer<TwitterRepo>(
builder: (context, model, child){
return ListView.builder(
itemCount: model.data.length,
itemBuilder: (context, index){
return Center(
child: Text(index.toString()),
);
});
},
),
);
}
}
If you want to use the callback to notify the UI to render some new data, you may want to use Future or Stream. Anyway, the question is how to implement a callback/listener so here I give you some examples:
You can't declare a variable using this, you could initialize the variable on the constructor
_TweetListState() {
_twitterRepo = TwitterRepo(this);
}
or inside initState()
#override
void initState() {
super.initState();
_twitterRepo = TwitterRepo(this);
}
A better way to do this would be:
final TwitterRepo _twitterRepo = TwitterRepo();
#override
void initState() {
super.initState();
_twitterRepo.listen(this);
}
And the listen method implemented on TwitterRepo would set the callback
You could also use VoidCallback instead of TwitterRepoCallback:
#override
void initState() {
super.initState();
_twitterRepo.listen(() {
// Do stuff
});
}
Or a callback function using Function(String a, int b) instead of TwitterRepoCallback
#override
void initState() {
super.initState();
_twitterRepo.listen((a, b) {
// Do stuff
});
}

flutter_bloc - how can i get value without BlocBuilder?

I'm still a beginner with streams and bloc pattern.
I would like to do following:
Trigger an event.
Based on the event get back a state with an object
Store this object as JSON in a database.
All examples are showing, how an object can be displayed in a widget with BlocBuilder. But I don't need to display the value, only get it and store it. I can't figure out how to get the value into a variable.
How can I do that? In the View class I'm dispatching the event, but now I need to know how to get the object in the state back without using BlocBuilder.
Here are the details:
Bloc
class SchoolBloc extends Bloc<SchoolEvent, SchoolState> {
final SchoolRepository _schoolRepository;
StreamSubscription _schoolSubscription;
SchoolBloc({#required SchoolRepository schoolRepository})
: assert(schoolRepository != null),
_schoolRepository = schoolRepository;
#override
SchoolState get initialState => SchoolsLoading();
#override
Stream<SchoolState> mapEventToState(SchoolEvent event) async* {
if (event is LoadSchool) {
yield* _mapLoadSchoolToState();
Stream<SchoolState> _mapLoadSchoolToState(LoadSchool event) async* {
_schoolSubscription?.cancel();
_schoolSubscription = _schoolRepository.school(event.id).listen(
(school) {
SchoolLoaded(school);
}
);
}
Event
#immutable
abstract class SchoolEvent extends Equatable {
SchoolEvent([List props = const []]) : super(props);
}
class LoadSchool extends SchoolEvent {
final String id;
LoadSchool(this.id) : super([id]);
#override
String toString() => 'LoadSchool';
}
State
#immutable
abstract class SchoolState extends Equatable {
SchoolState([List props = const []]) : super(props);
}
class SchoolLoaded extends SchoolState {
final School school;
SchoolLoaded([this.school]) : super([school]);
#override
String toString() => 'SchoolLoaded { school: $school}';
}
View
class CourseView extends StatefulWidget {
#override
State<StatefulWidget> createState() => _CourseViewState();
}
class _CourseViewState extends State<CourseView> {
#override
initState() {
super.initState();
print("this is my init text");
final _schoolBloc = BlocProvider.of<SchoolBloc>(context);
_schoolBloc.dispatch(LoadSchool("3kRHuyk20UggHwm4wrUI"));
// Here I want to get back the school object and save it to a db
}
Test that fails
For testing purposes I have done following:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:teach_mob/core/blocs/school/school.dart';
class CourseView extends StatefulWidget {
#override
State<StatefulWidget> createState() => _CourseViewState();
}
class _CourseViewState extends State<CourseView> {
#override
void initState() {
super.initState();
BlocProvider.of<SchoolBloc>(context)
.dispatch(LoadSchool("3kRHuyk20UggHwm4wrUI"));
}
#override
Widget build(BuildContext context) {
return BlocListener<SchoolBloc, SchoolState>(
listener: (context, state) {
print("BlocListener is triggered");
},
child: Text("This is a test")
);
}
}
The LoadSchool event is triggered. The text in the child attribute of BlocListener is displayed, but the listener function that should print "BlocListener is triggered" is not executed.
Use BlocListener. It is meant to be used for those cases you mention.

What is the equivalent of Android LiveData in Flutter?

Android's LiveData allows to update the UI when the activity is in an active state. So if a background operation has finished while the activity is paused, the activity won't be notified and thus the app won't crush. Can Flutter perform the same behavior?
For people interested in an equivalent of LiveData for other scenarios, I present you StreamController:
class ExampleViewModel {
StreamController<bool> loggedInStream = StreamController<bool>();
logIn() { loggedInStream.add(true); }
}
class ExampleScreen extends StatefulWidget {
#override
State<StatefulWidget> createState() => ExampleScreenState();
}
class ExampleScreenState extends State<ExampleScreen> {
ExampleViewModel _viewModel;
BuildContext _ctx;
#override
void initState() {
super.initState();
_viewModel = ExampleViewModel()
_viewModel.loggedInStream.stream.listen( (loggedIn) {
if ( loggedIn != null && loggedIn ) {
Navigator.of(_ctx).pushReplacementNamed("/home");
}
});
}
#override
Widget build(BuildContext context) {
_ctx = context;
var loginBtn =
RaisedButton(
onPressed: _viewModel.logIn(true),
child: Text(
"LOGIN",
style: new TextStyle(
fontSize: 24.0,
)
),
color: Colors.green,
textColor: Colors.white,
);
return loginBtn;
}
#override
void dispose() {
super.dispose();
_viewModel.loggedInStream.close();
}
}
You can subscribe to it just like a LiveData, using:
loggedInStream.stream.listen( (data) { code } )
And you should clear the listeners in dispose to avoid memory leaks:
loggedInStream.close()
This code basically do the following things:
Creates a screen with a button.
Listen to a Stream (observe a LiveData).
When you click the button, it changes the value.
The listener (observer) is triggered.
Launches new screen.
You can use WidgetsBindingObserver to listen to the application state.
class AppLifecycleReactor extends StatefulWidget {
const AppLifecycleReactor({ Key key }) : super(key: key);
#override
_AppLifecycleReactorState createState() => new _AppLifecycleReactorState();
}
class _AppLifecycleReactorState extends State<AppLifecycleReactor> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
AppLifecycleState _notification;
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() { _notification = state; });
}
#override
Widget build(BuildContext context) {
return new Text('Last notification: $_notification');
}
}
Easy: Flutterx Live Data
There is no need to observe App lifecycle: widget are built only when the app is resumend
This library integrates perfectly LiveData concepts, also is well documented.
Is developed on Flutter 1.14.x-dev you need master flutter channel at the moment
Sorry to be late for the party,
My colleague and I have developed a library that mimics the live data of android, on flutter.
Check it out:
https://pub.dev/packages/stream_live_data