SignalR client flutter library shows null values in another widget when using Flutter Provider - flutter

Am trying to connect on a SignalR server with flutter, and I tried to use Provider to access the data which comes to another Widget, but am receiving a null value. This is the SignalR library for flutter am using.
Below is my Class which extends a ChangeNotifier :
class BreakingNewsSignalRClient with ChangeNotifier {
String _breakingNews;
Future<void> getBreakingNews() async {
final hubConnection = HubConnectionBuilder()
.withUrl('https://services.xxxx.com/breakingNews')
.build();
await hubConnection.start();
hubConnection.on("UpdateBreakingNews", (data){
_breakingNews = data.toString();
print(data.toString());
notifyListeners();
});
hubConnection.onclose((error) => print("Connection Closed"));
}
String get breakingNewsFunction => _breakingNews;
}
class BreakingNewsModel {
final String News;
BreakingNewsModel(this.News);
}
As you can see the code above, am having a getter String get breakingNewsFunction => _breakingNews;, this is called in another Stateful Widget to populate some data from the SignalR server, but it returns null in the other widget, however, when you try to print data here in the getBreakingNews method the data is shown.
Below is another Widget class which receives the data :
class BreakingNews extends StatefulWidget {
const BreakingNews({Key key, #required this.article, #required this.index})
: super(key: key);
final Article article;
final int index;
#override
_BreakingNewsState createState() => _BreakingNewsState();
}
class _BreakingNewsState extends State<BreakingNews> {
settings() async{
var breakingNewsInfo =
Provider.of<BreakingNewsSignalRClient>(context, listen: false);
await breakingNewsInfo.getBreakingNews();
print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');
}
#override
void initState() {
// TODO: implement initState
settings();
super.initState();
}
}
So when you look at this line print('lets see -- ${breakingNewsInfo.breakingNewsFunction}');, it prints null, am still wondering what am doing wrong here.
Kindly need some help.

Did you try data[0]?
Can you write it?
_breakingNews = data[0].toString();
instead of
_breakingNews = data.toString();

This is pretty simple. Your Future<void> getBreakingNews() async returns void and therefore null. Just adjust the return type to whatever you need.
Edit:
Actually, the problem is you are not calling your getter here.
await breakingNewsInfo.getBreakingNews();
So either return your result from the function, or call the getter. Either should work.

Related

Flutter Cubit InitState

I am at the begin of my Cubit learning and i tried to create a "Liter-Tracker" with sharedPrefs. Everything works but not the init state. I have to press a Button first because I initialize the drinkValue with 0. I tried to return an Int with the value from shared prefs but this dont work :( May you help me?
This is my cubit:
class DrinkCubit extends Cubit<DrinkState> {
DrinkCubit() : super(DrinkState(drinkValue: 0));
Future<void> loadCounter() async {
final prefs = await SharedPreferences.getInstance();
state.drinkValue = (prefs.getInt('counter') ?? 0);
}
Future<int> loadInitCounter() async {
final prefs = await SharedPreferences.getInstance();
return state.drinkValue = (prefs.getInt('counter') ?? 0);
}
}
and this my cubit state:
class DrinkState {
int drinkValue;
int? amount;
DrinkState({
required this.drinkValue,
});
}
I also tried something like this in my MainPage, how i usually init my state with setState:
#override
void initState() {
super.initState();
BlocProvider.of<DrinkCubit>(context).loadCounter();
}
Context is not accessible in initstate, try using didChangeDependencies life cycle method Flutter get context in initState method
Firstly, I strongly advise you to avoid the StatefulWidget when you use BLoC, but it doesn't mean you can't use it at all. Just be careful because setState() can rebuild BlocProvider inside the stateful widget.
As for the initialization process, I suggest you use this approach on the BlocProvider.
class DrinkScreen extends StatelessWidget {
const DrinkScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => DrinkCubit()..loadCounter(), // Or call other initialization method
child: DrinkView(),
);
}
}
This approach works really well if you reuse this screen multiple times, for example, you redirect to DrinkScreen every time you want to fill data and you dispose of the screen afterward (Navigate.pop(), etc). This way you can automatically initialize the cubit every time you redirect into this screen, you don't need to use StatefulWidget to init the cubit.

Get data from an asynchronous method?

I am having big time trouble with getting the data from my method getAllPlayerData() outside the .then((Value).
The "bad print" in my code shows up before my "good print". I want to be able to use my variable "TheStats" in my code with all the data inside of it and not a "null" value or "instance of Future<Dynamic"
import 'package:cleanmaybe/Controllers/players_controller.dart';
import 'package:flutter/material.dart';
class PlayerStatView extends StatefulWidget {
const PlayerStatView({Key? key}) : super(key: key);
#override
State<PlayerStatView> createState() => _PlayerStatViewState();
}
class _PlayerStatViewState extends State<PlayerStatView> {
var theStats;
#override
void initState() {
getAllPlayerData().then((value) {
theStats = value;
print("Good print $theStats");
});
super.initState();
}
#override
Widget build(BuildContext context) {
print("bad print $theStats");
return Container();
}
}
This is my "getAllPlayerData()"
getAllPlayerData() async{
SharedPreferences sp = await _pref;
int? id = sp.getInt('idPlayer');
final statAccess = DatabaseModel();
var lookForData = await statAccess.getPlayerData(id);
return lookForData;
}
And if you need it, this is my getPlayerData(id) method that fetches all the data from my database:
Future getPlayerData(idPlayer) async {
var dbCoach = await db;
var dataPlayer = dbCoach!.rawQuery("SELECT * FROM players WHERE"
" idPlayer = '$idPlayer'");
return dataPlayer;
}
Your "bad print" will practically always run before "Good print" as long as getAllPlayerData() take any time at all to process with this construct.
There are several ways to handle this, but a suggestion is to use the FutureBuilder widget for it (as #pskink just wrote as a comment :)) Check the offical documentation.

Sending multiple controllers as one controller

class OtpScreen extends StatefulWidget {
const OtpScreen({Key? key}) : super(key: key);
#override
State<OtpScreen> createState() => _OtpScreenState();
}
class _OtpScreenState extends State<OtpScreen> {
late String otp1;
late String otp2;
late String otp3;
late String otp4;
late String otp5;
late String otp6;
late OtpModel _futureotp;
final _formKey = GlobalKey<FormState>();
late TextEditingController controller;
#override
void initState() {
controller = TextEditingController();
controller.addListener(() {
_onChanged();
});
super.initState();
}
#override
void dispose() {
controller.dispose();
super.dispose();
}
void _onChanged() {
try {
dynamic sendotp = ApiHelper().otpValidation();
if (sendotp is OtpModel) {
setState(() {
_futureotp = sendotp;
});
}
if (sendotp.status == 'success') {
Routes.sailor.navigate(
'/signIn',
navigationType: NavigationType.pushAndRemoveUntil,
removeUntilPredicate: (routes) => false,
transitions: [SailorTransition.fade_in],
);
} else {
print(sendotp.message);
}
} catch (e) {
print(e);
}
}
[1
I'm trying to create an OTP screen with textformfield and I have 6 TextEditingControllers but I want to send the value I get from them as one controller. How can I do that? and How would you do it if you were to create an otp screen that sends data to the backend without a button?
p.s: I tried creating 6 controllers and putting an adlistener at the last controller.
If you want to use in-built dart functionality, then try using Expando feature. It basically allows you to attach values to existing objects. Now this may not be answer that you are looking for but it certainly helps to know more than one way to tackle an issue.
If you want to know more about Expando feature than consider checking out this question.
However, you should be careful while adding an object to Expando as they effectively stays alive forever. This is what it says in official docs:
There is no restriction on other classes, even for compile time constant objects. Be careful if adding expando properties to compile time constants, since they will stay alive forever.
you can try pinput, or check their implementation and try to make your own one.

How to get id from StatefulWidget in State?

I want to load comments in my post here. For that I need to send post id to my HTTP get request. post id I sent from another page. but I want to assign that String id; value to final response = await http.get("http://$ip:$apiPort/solutions/$id"); here id in Flutter.
How can I do that?
to clear what I want
my code is
class Solutions extends StatefulWidget {
String id ;
final bool isEditMode;
Solutions({
this.id,
this.isEditMode,
});
#override
_SolutionsState createState() => _SolutionsState();
}
class _SolutionsState extends State<Solutions> {
List data;
var ip = Configuration.yourIp;
var apiPort = Configuration.yourApiPort;
Future<List> getData() async {
final response = await http.get("http://$ip:$apiPort/solutions/$id");
return json.decode(response.body);
}
#override
void initState() {
super.initState();
this.getData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
Future<List> getData() async {
final response = await http.get("http://$ip:$apiPort/solutions/${widget.id}");
return json.decode(response.body);
}
This should to the trick.
From the State class (_SolutionState in your case), you can access the widget (Solution in your case) members by finding them in widget.
BONUS
Your id should be final, since StatefulWidget is marked as an immutable class, which means its members should all be final. You have surely a warning about this from your IDE.

Flutter: A variable passed to the next screen becomes null

I want to pass a variable to the next screen but it becomes null in the next screen. what am I wrong with this? My code is like below.
first_screen.dart
onTap: () {
print(doc); // Prints out Instance of `DocumentSnapshot` on log
Navigator.of(context).pushNamed('/second', arguments: doc);
},
second_screen.dart
class SecondScreen extends StatefulWidget {
final DocumentSnapshot doc;
SecondScreen({
this.doc,
});
#override
State<StatefulWidget> createState() {
return _SecondScreenStateState();
}
}
class _SecondScreenState extends State<SecondScreen> {
#override
void initState() {
super.initState();
print(widget.doc); // Prints out null here
}
I tried with othe data types but all variables become null in the next screen.
You have to pass argument like this:
Navigator.of(context).pushNamed('/second', arguments: doc);
for you is true but, use the ModalRoute.of() method to returns the current route with the arguments like this:
final doc = ModalRoute.of(context).settings.arguments as String;
I assumed that doc is String.
If you're sharing data between widgets (screens) try looking at InheritedWidget.Take a look at https://www.youtube.com/watch?v=1t-8rBCGBYw. You can also look at state management packages like provider which is pretty easy to understand or a bloc. These will save you in the long run.