WebSocketChannel becomes null when passed to a StatefulWidget's State class - flutter

I have a simple client code in which I'm trying to pass the WebSocketChannel instance to an inner stateful widget, and for some reason when I try to run the code the app crushes and displays on the screen "Unexpected null value. See also: https://flutter.dev/docs/testing/errors". It would be greatly appreciated if someone could explain to me why this happens and how to fix it.
The code:
import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class TestWidget extends StatefulWidget {
final WebSocketChannel channel;
const TestWidget(this.channel);
#override
_TestWidgetState createState() => _TestWidgetState();
}
class _TestWidgetState extends State<TestWidget> {
String buttonText = '';
_TestWidgetState() {
widget.channel.stream.listen((data){
setState(() {buttonText = data;});
});
}
#override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: (){widget.channel.sink.add('hello');},
child: Text(buttonText)
);
}
}
class App extends StatelessWidget {
final WebSocketChannel channel = WebSocketChannel.connect(
Uri.parse('ws://localhost:8000/')
);
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body:
TestWidget(channel)
));
}
}
void main() {
runApp(App());
}
Thanks in advance for the help.

Any particular reason why you put
final WebSocketChannel channel = WebSocketChannel.connect(
Uri.parse('ws://localhost:8000/')
);
in App? Move this line code to TestStateWidget constructor. It's best practice u follow null safety method when try to access an object.

Related

Where is a correct place to initialize get_it in widget?

I need to initialize get_it right in the widget file so that it would be available down the widget three. Normally we would do it next to runApp() but here I need it for demonstration purposes.
Is it a correct place to initialize it?
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'form_store.dart';
final getIt = GetIt.instance;
class FormExample extends StatefulWidget {
const FormExample();
#override
_FormExampleState createState() => _FormExampleState();
}
class _FormExampleState extends State<FormExample> {
final store = getIt.get<FormStore>();
#override
void initState() {
super.initState();
getIt.registerSingleton<FormStore>(FormStore());
store.setupValidations();
}
...
// in the same .dart file
// this widget will be used in build method of the above FormExample class
class InnerPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
final store = GetIt.I<FormStore>();
return Scaffold(
body: Center(child: Text('Hello ${store.name}')),
);
}
}

How to use stateful widget parameters in state class at construction without adding the widget to the tree?

I stumped into a problem where I need to use a StatefulWidget parameter in its state class when it's constructed, but I couldn't find a way to do it since using widget.[whatever variable name] in the state's class constructor returns an unexpected null value, and the initState function only runs when the widget is being drawn to the screen.
For example:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
final String text;
Test(this.text);
final state = _TestState();
#override
_TestState createState() => state;
}
class _TestState extends State<Test> {
String? changingText;
void updateChangingText(String moreText){
changingText = changingText! + moreText;
}
#override
void initState() {
super.initState();
changingText = widget.text;
}
#override
Widget build(BuildContext context) {
return Text(changingText!);
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
var w = Test('test');
w.state.updateChangingText(' text');
return MaterialApp(home: Scaffold(body:
Test('test text')
));
}
}
void main() {
runApp(App());
}
This doesn't work since changingText is being updated before initState gives it its initial value since it only runs when Text is being drawn to the screen and this:
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
final String text;
Test(this.text);
final state = _TestState();
#override
_TestState createState() => state;
}
class _TestState extends State<Test> {
String? changingText;
void updateChangingText(String moreText){
changingText = changingText! + moreText;
}
_TestState(){
changingText = widget.text;
}
#override
Widget build(BuildContext context) {
return Text(changingText!);
}
}
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
var w = Test('test');
w.state.updateChangingText(' text');
return MaterialApp(home: Scaffold(body:
Test('test text')
));
}
}
void main() {
runApp(App());
}
doesn't work either since you can't use widget.[whatever] in state class constructors (for some reason).
So what can I do to use widget parameters in the state class before the widget is drawn to the screen?
Thanks in advance for the help
You should use the initState method present in the State for this instead of the constructor
#override
void initState() {
changingText = widget.text;
super.initState();
}

Riverpod ProviderListener - 'StateNotifierProvider<Auth, bool>' can't be assigned to 'ProviderBase<Object, StateController<bool>>'

I'm trying to use a ProviderListener from Riverpod to listen to my authProvider and control the page displayed if a user is authorized or not. I'm getting the error:
error: The argument type 'StateNotifierProvider<Auth, bool>' can't be assigned to the parameter type 'ProviderBase<Object, StateController>'.
The error shows up on the: provider: authProvider, inside the ProviderListener
I'm wondering if it's due to the update on StateNotifierProvider?
I would like to know how to use the ProviderListener better even if there's a better way to handle the authorization flow (I'm VERY open to feedback and criticism and greatly appreciate any time a person can take to help). I cut out non-relevant code
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class Auth extends StateNotifier<bool> {
Auth() : super(false);
void setAuth(bool auth) {
state = auth;
}
}
final authProvider = StateNotifierProvider<Auth, bool>((ref) => Auth());
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
ProviderScope(
child: MyApp(),
),
);
}
class MyApp extends StatefulHookWidget {
// const MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Future<FirebaseApp> _fbMyApp = Firebase.initializeApp();
Widget route = SplashScreen();
#override
Widget build(BuildContext context) {
return ProviderListener<StateController<bool>>(
provider: authProvider,
onChange: (context, auth) {
if (auth.state = true) {
route = HomeScreen();
} else {
route = SplashScreen();
}
},
child: MaterialApp(
home: route,
);
}
}
I managed to get it to sort of work by changing to:
return ProviderListener<StateNotifier<bool>>(
provider: authProvider.notifier,
it's giving me a non-breaking error of:
info: The member 'state' can only be used within instance members of subclasses of 'package:state_notifier/state_notifier.dart'. (invalid_use_of_protected_member)
and not working properly - the state isn't being updated when I'm using a context.read
context.read(authProvider.notifier).state = true;
So it's buggy but not fully broken. At least it's some progress. I would still love help and any feedback anyone wants to give!
Remove StateController from ProviderListener, leave only the type (bool in this case)
return ProviderListener<bool>(
provider: authProvider, //this will read the state of your provider (a bool state)
onChange: (context, auth) {
if (auth) { //remove setter auth = true, it doesn't make sense to set a value inside an if
route = HomeScreen();
} else {
route = SplashScreen();
}
},
child: MaterialApp(
home: route,
);
This way you're reading the state of your StateNotifier

Error: The getter 'title' isn't defined for the type 'RSSParser'

I wanted to implements a simple tutorial of how parsing an Rss Feed with Flutter, here is my code:
import 'package:flutter/material.dart';
import 'package:webfeed/webfeed.dart';
import 'package:http/http.dart' as http;
import 'package:url_launcher/url_launcher.dart';
import 'package:cached_network_image/cached_network_image.dart';
class RSSParser extends StatefulWidget {
#override
_RSSParserState createState() => _RSSParserState();
}
class _RSSParserState extends State<RSSParser> {
final String url = "https://www.90min.com/posts.rss";
RssFeed _feed;
String _title;
static const String loadingFeedMsg = 'Loading Feed...';
static const String feedLoadErrorMsg = 'Error Loading Feed.';
static const String feedOpenErrorMsg = 'Error Opening Feed.';
Future<RssFeed> loadFeed() async{
try{
final client = http.Client();
final response = await client.get(url);
return RssFeed.parse(response.body);
}
catch(e){
}
return null;
}
updateTitle(title){
setState(() {
_title = title;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
updateTitle(widget.title);
}
updateFeed(feed){
setState(() {
_feed = feed;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_title),
),
);
}
}
The problem is that i got a compilation error in that instruction
updateTitle(widget.title);
with the following error message:
The getter 'title' isn't defined for the type 'RSSParser'
In the tutorial, it works fine!!
Do you have an idea how to solve this?
Thank you
You haven't declared title for you RSS widget. It should look something like ths:
class RSSParser extends StatefulWidget {
final String title;
const RSSParser({required this.title});
This should solve your error.
This is not working because there is not title in RSS class.
I think you are not clear with use of widget.something. It means that in the class which extends StatefulWidget there is a something parameter which i need to get in stateObject.
See the code to understand.
class YellowBird extends StatefulWidget {
const YellowBird({ Key? key }) : super(key: key);
String someData = 'SomeData'; // Some data
#override
_YellowBirdState createState() => _YellowBirdState();
}
//This is the state object
class _YellowBirdState extends State<YellowBird> {
// Now that if you need some data from the above class. You use use this widget.someData to get it here
String getHere = widget.someData ;
#override
Widget build(BuildContext context) {
return Container(color: const Color(0xFFFFE306));
}
}

Initialize StateProvider in Widget

I just want initialize provider only one time with widget param. For some reason I can't use .family. I'm not sure if this is the right way. Can you check this? Thank you.
StateProvider<String> valueStateProvider;
class Widget extends HookWidget {
final String value;
Widget({#required this.value}) {
valueStateProvider = StateProvider<String>((ref) => this.value);
}
}
Finally I found the right way to do this. There is special provider type for this situation. ScopedProvider.
final scopedProvider = ScopedProvider<String>((_) => throw UnimplementedError());
class Widget extends HookWidget {
Widget({#required this.value});
final String value;
#override
Widget build(BuildContext context) {
return ProviderScope(
overrides: [scopedProvider.overrideWithValue(this.value)],
child: AnotherWidget()
);
}
}
So you can use scopedProvider in AnotherWidget. Yay!