How to find a controller using GetX in flutter - flutter-getx

I am trying to use Get.find to use LessonListController, but flutter tells me error,
throw '"$S" not found. You need to call "Get.put($S())" or "Get.lazyPut(()=>$S())"'
below is Lessonlistcontroller
class LessonListController extends GetxService {
final LessonListRepo lessonListRepo;
LessonListController({required this.lessonListRepo});
List<dynamic> _lessonList = [];
List<dynamic> get lessonList => _lessonList;
Future<void> getLessonList() async {
Response response = await lessonListRepo.getLessonList();
if (response.statusCode == 200) {
print('got you');
_lessonList = [];
_lessonList.addAll(Course.fromJson(response.body).lessons);
// update();
//update
} else {}
}
}
dependencies as below,
Future<void> init() async {
//api client
Get.lazyPut(() => ApiClient(appBaseUrl: AppConstants.BASE_URL));
//repos
Get.lazyPut(() => LessonListRepo(apiClient: Get.find()));
//controllers
Get.lazyPut(() => LessonListController(lessonListRepo: Get.find()));
}
here is the main.dart file
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
Get.find<LessonListController>().getLessonList();
// Get.lazyPut<LessonListController>(() =>get.() {
// };
return const GetMaterialApp(
debugShowCheckedModeBanner: false,
home: Diary(),
);
}
}
Thank you very much.

You haven't initialized the LessonListController using Get.put(LessonListController());
Get.find() is used to get the already initialized instance of Created controller.
GetxControlled works as Singleton, So it finds the already created instance every time you call Get.find() , Get.find() will only work if you have previously called Get.put or Get.lazyPut

Related

make hubconnection.start() only once in the whole app

I am using the singalR package in order to use websocket in my application. During the writing of my program, I was advised to use await hubconnection.start only once and throughout the whole application. Is it possible? I thought that I could run this task somehow in the background and not make every websocket request start every time I want to use some kind of request. Is this possible or is this the wrong idea?
now I an trying to split up the whole thing and found out that in initState we can create method which will be start first before all others code in app, so I decided to split it up like that(in educational purpose I declaired connection setting globally in my file)
final httpConnectionOptions = HttpConnectionOptions(
accessTokenFactory: () => SharedPreferenceService().loginWithToken(),
skipNegotiation: true,
transport: HttpTransportType.WebSockets);
final hubConnection = HubConnectionBuilder()
.withUrl(
'http://secureLink/mysocket',
options: httpConnectionOptions,
)
.build();
this is my class:
class TestClass extends StatefulWidget {
bool? gpsenabled;
TestClass({Key? key}) : super(key: key);
#override
State<TestClass> createState() => _TestClassState();
}
class _TestClassState extends State<TestClass> {
void checkGPS() async {
if (hubConnection.state == HubConnectionState.Connected) {
await hubConnection.invoke('GPSEnable').then((value) {
setState(() {
widget.gpsenabled = value as bool;
});
print(widget.gpsenabled);
Future.delayed(Duration(seconds: 5));
checkGPS();
});
}
}
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
hubConnection.start();
print('connection first');
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(

How to move Future methods from Stateful widget to their own class Flutter

I have several Future methods contained within a Stateful widget. Identical methods appear in different parts of the app. I want to add the methods to one Class instead of rewriting four times in the app but I haven't been able to figure it out because several of the methods call setState to update the UI. The methods are called when users choose an image from their gallery, choose to take a photo, upload the selected image to the database for display in the app, the image is compressed, etc.
class ExampleClass extends StatefulWidget {
const ExampleClass({Key? key}) : super(key: key);
#override
State<ExampleClass> createState() => _ExampleClassState();
}
class _ExampleClassState extends State<ExampleClass> {
File? file;
Future<void> _captureImageWithCamera() async {
Get.back();
XFile? pickedFile = await ImagePicker().pickImage(
source: ImageSource.camera,
);
setState(() {
file = File(pickedFile!.path);
});
}
#override
Widget build(BuildContext context) {
return Container();
}
}
I have only included one of the methods (captureImageWithCamera) assuming that a suggested solution for one could be applied to all of them. If I need to provide more code I will be happy to do so. Any help will be greatly appreciated.
when you are using third party library to something outside your app like picking a photo, downloading some data, making http calls.
consider to create service class for this stuff, service class is basically this:
a class with single responsibility, like this class should do only one thing.
in your case you should create ImagePickerService the class job is pick an Image and return it to you nothing more, you should not call any flutter framework inside it
like setState because it's not his job to update the UI.
class ImagePickerService {
//make this class singleton so you do not make a new instance every time you want it.
static final ImagePickerService _instance = ImagePickerService._();
ImagePickerService._();
factory ImagePickerService() => _instance;
Future<XFile?> pickImage(ImageSource imageSource) async {
return await ImagePicker().pickImage(
source: imageSource,
);
}
}
so now when ever you want to pick an Image you just need to call this service like this.
onTap: () async {
final file = await ImagePickerService().pickImage(ImageSource.camera);
setState(() {
// you got the file boss do anything you want
});
}
now when you create a new page you just create page and defined some services inside it.
You can return pickedFile from your extracted method, then each class which uses the result can call setState itself, using the returned value.
class ImageCapturer {
Future<XFile?> captureImageWithCamera() async {
Get.back();
XFile? pickedFile = await ImagePicker().pickImage(
source: ImageSource.camera,
);
return pickedFile;
}
}
class ExampleClass extends StatefulWidget {
const ExampleClass({Key? key}) : super(key: key);
#override
State<ExampleClass> createState() => _ExampleClassState();
}
class _ExampleClassState extends State<ExampleClass> {
File? file;
Future<void> _captureImageWithCamera() async {
final file = await ImageCapturer().captureImageWithCamera();
setState(() {
this.file = File(file!.path);;
});
}
#override
Widget build(BuildContext context) {
return Container();
}
}
You could abstract the code away to one function with a callback parameter, like so:
class OtherExampleClass {
Future<void> captureImageWithCamera(
void Function(XFile? pickedFile) callback,
) async {
Get.back();
XFile? pickedFile = await ImagePicker().pickImage(
source: ImageSource.camera,
);
callback(pickedFile);
}
}
class ExampleClass extends StatefulWidget {
const ExampleClass({Key? key}) : super(key: key);
#override
State<ExampleClass> createState() => _ExampleClassState();
}
class _ExampleClassState extends State<ExampleClass> {
File? file;
Future<void> _captureImageWithCamera() async {
await OtherExampleClass().captureImageWithCamera((XFile? pickedFile) {
setState(() {
file = File(pickedFile!.path);
});
}
);
}
#override
Widget build(BuildContext context) {
return Container();
}
}

Hive for flutter just returns the Instance instead of the actual value

I have decided to go with hive as my settings/preference storage. However, I am not able to implement my Storage class correctly because the getValue method always returns Instance of 'Future<dynamic>' instead of the actual value. Does anyone know how to fix that?
My Storage class just contains the getValue and setValue which always opens the hive box and then either should set or get the value. Also, I have created the enum StorageKeys in order to have a set of keys and make sure I get or set the value to the deticated key.
main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
routes: {
"/": (context) => const Home(),
},
));
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
get() async {
return await Storage.getValue(StorageKeys.authTokenKey);
}
void set() async {
await Storage.setValue(StorageKeys.authTokenKey, 'TestValue');
}
#override
Widget build(BuildContext context) {
set();
print(get());
return Scaffold(
backgroundColor: Colors.white,
appBar: ChevronNavigation(),
body: Container(),
);
}
}
storage.dart
class Storage {
static const preferencesBox = '_storageBox';
static Future<void> setValue(StorageKeys key, dynamic value) async {
final storage = await Hive.openBox<dynamic>(preferencesBox);
storage.put(key.toString(), value);
}
static dynamic getValue(StorageKeys key) async {
final storage = await Hive.openBox<dynamic>(preferencesBox);
return await storage.get(key.toString(), defaultValue: null) as dynamic;
}
}
enum StorageKeys {
authTokenKey,
}
print(get()); will give you Instance of Future<dynamic> since get() returns a Future object.
SOLUTION:
You need to await the actual value in the Future object by writing await before get() in a Future method.
Like this:
print(await get());
In your question above, this cannot work as the build method cannot be async. You can put the print(await get()) in a separate method and have it in your initState.
Like this:
#override
void initState() {
super.initState();
callGet();
}
Future<void> callGet() async {
print(await get());
}
You are printing the await Storage.getValue(StorageKeys.authTokenKey); value, and as it is a Future, you get this message.
You should try to call it on your initState and then get the Hive value. When the value returns you cant print it.
Eg:
#override
void initState() {
super.initState();
Storage.getValue(StorageKeys.authTokenKey).then((value) => print(value));
}

How to make methods in Future class Flutter

how do I make a method inside Future class? I wanted to call the methods in other dart files, but because I made the functions as Future, it does not get called by the instance name.
This is the example code that I want to call in another dart file.
Future tokenDb() async{
final database = openDatabase(
join(await getDatabasesPath(), 'token_list.db'),
onCreate: (db, version) {
return db.execute(
"CREATE TABLE tokens (token INTEGER PRIMARY KEY)",
);
},
version: 1,
);
Future<void> insertToken(Token token) async {
final Database db = await database;
await db.insert(
'tokens',
token.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
}
and I need it here:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MaterialApp(home: MyApp()),
);
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late FirebaseMessaging messaging;
String tokenValue = "";
#override
void initState() {
messaging = FirebaseMessaging.instance;
messaging.getToken().then((value) {
tokenValue = value!;
Clipboard.setData(ClipboardData(text: tokenValue));
print(tokenValue);
var user1 = Token(token: tokenValue);
print("user1 token : " + tokenValue);
**var db = tokenDb();
db.insertToken(user1);
tokenDb();**
// Maybe I need to call the function here?
});
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: AuthTypeSelector(),
),
);
}
}
I don't have a lot of knowledge of Flutter... Thanks for your help!! I really appreciate it. :)

Flutter How to listen variable from other class

I want to listen variable from other class in flutter, so always to know if the app has internet connection
I have the streamcontroller in checknetwork.dart and listen in home.dart
I dont get anything at print statement
CheckNetwork.dart
class NetworkCheck {
var streamController = StreamController();
Future<bool> check() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
streamController.sink.add(true);
streamController.close();
return true;
} else if (connectivityResult == ConnectivityResult.wifi) {
streamController.sink.add(true);
streamController.close();
return true;
}
return false;
}
dynamic checkInternet(Function func) {
check().then((internet) {
if (internet != null && internet) {
func(true);
}
else{
func(false);
}
});
}
}
Home.dart
class Home extends StatefulWidget {
const Home({Key key}) : super(key: key);
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home>{
Future _checkNetwork() async {
NetworkCheck networkcheck = NetworkCheck();
networkcheck.streamController.stream.listen((data) {
print('Got! $data');
});
}
You need to have a state management System to get the stream controller accessible to all classes and it would be the efficient way , but if you still want your current code to work than call check method of NetworkCheck class before listening to the Stream .
So modify your home.dart like this:
class Home extends StatefulWidget {
const Home({Key key}) : super(key: key);
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home>{
Future _checkNetwork() async {
NetworkCheck networkcheck = NetworkCheck();
//this will provide value in your stream.
networkcheck.check();
networkcheck.streamController.stream.listen((data) {
print('Got! $data');
});
}