"GetXController" not found. You need to call? - flutter

I want to implement GetX binding methods, and as result, I got this error:
════════ Exception caught by widgets library ═══════════════════════════════════
The following message was thrown building MyApp(dirty):
"UserController" not found. You need to call "Get.put(UserController())" or "Get.lazyPut(()=>UserController())"
The relevant error-causing widget was
MyApp
lib/main.dart:9
main.dart
import 'package:donirajkrv/views/welcome.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import './router/index.dart';
import './bindings/user_binding.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
initialBinding: UserBinding(),
home: const Scaffold(
backgroundColor: Color.fromRGBO(244, 248, 252, 1),
body: SafeArea(child: WelcomePage()),
),
getPages: [...routeList],
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: const Color(0XFFFB6394),
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.dark,
),
),
);
}
}
How main works:
void main() {
Get.put<UserController>(UserController());
runApp(const MyApp());
}
user_binding.dart
import 'package:get/get.dart';
import 'package:donirajkrv/controllers/user_controller.dart';
class UserBinding implements Bindings {
#override
void dependencies() {
Get.put(() => UserController());
}
}
Routelist:
import 'package:get/get.dart';
import '../views/welcome.dart';
import '../views/login.dart';
import '../views/register.dart';
import '../views/home_page.dart';
import '../middlewares/auth_middleware.dart';
import '../middlewares/back_to_home_middleware.dart';
// Routes path
import '../routes/index.dart';
List routeList = [
GetPage(
name: '/${Routes.WELCOME_PAGE}',
page: () => const WelcomePage(),
middlewares: [BackToHomeMiddleware()]),
GetPage(
name: '/${Routes.LOGIN_PAGE}',
page: () => const Login(),
middlewares: [BackToHomeMiddleware()]),
GetPage(
name: '/${Routes.REGISTER_PAGE}',
page: () => const Register(),
middlewares: [BackToHomeMiddleware()]),
GetPage(
name: '/${Routes.HOME_PAGE}',
page: () => const HomePage(),
middlewares: [AuthMiddleware()])
];

i have added the detail code for binding controller and increment function so try using this, it will definitely work
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
initialBinding: UserBinding(),
getPages: [...routeList],
initialRoute: RouteNames.welcomePage,
home: const Scaffold(
backgroundColor: Color.fromRGBO(244, 248, 252, 1),
),
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: const Color(0XFFFB6394),
appBarTheme: const AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.dark,
),
),
);
}
}
class UserController extends GetxController {
var i = 0.obs;
void increment(){
i++;
}
}
class RouteNames {
static String welcomePage = "/welcomePage";
}
List routeList = [
GetPage(
name: '/${RouteNames.welcomePage}',
page: () => WelcomePage(),
),
];
//here use binding like this
class UserBinding implements Bindings {
#override
void dependencies() {
Get.put<UserController>(UserController(), permanent: true);
}
}
class WelcomePage extends StatelessWidget {
WelcomePage({Key? key}) : super(key: key);
// final userController = Get.put(UserController());
final userController = Get.find<UserController>();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
),
body: Column(
children: [
Obx(() => Text(userController.i.value.toString(),
style: const TextStyle(
fontSize: 25,
color: Colors.blueAccent
),),),
ElevatedButton(onPressed: (){
userController.increment();
}, child: const Text("Button")),
],
),
);
}
}

you should make a simple change
Instead of
GetMaterialApp(
initialBinding: BindingsBuilder(() {
Get.put(UserBinding());
})...
use
initialBinding: UserBinding())...
GetMaterialApp(
initialBinding: UserBinding())...
In the user_binding.dart file you can initiate every controller that your app uses.
and then you can use it by simply declare a variable like this
UserController controller = Get.find();
//or like this:
final controller = Get.find<UserController>();
//I haven't tested the second one, but it should work.
Hope it works :)
You can also follow the example below:
https://github.com/Prosa/Flutter-GetX-Bindings-Example

Related

Flutter - How to call an API every n minutes?

I need to call an API every n minutes. The data should be available across all screens. How can I implement this at app level. I am not using any state management tools.
void main() {
periodicSub = Stream.periodic(const Duration(seconds: 10))
.listen((_) {
///fetch data
someFuture =
Future<List<someObject>>.delayed(
const Duration(milliseconds: 500), () async {
return someFunction();
});
});
someFuntions returns a list. I want a certain FutureBuilder on HomePage to execute whenever the list is updated.
Here is an example using "https://pub.dev/packages/provider"
First create a Notifier:
import 'dart:async';
import 'package:flutter/material.dart';
class CustomNotifier with ChangeNotifier {
int counter = 0;
CustomNotifier() {
Stream.periodic(const Duration(seconds: 10)).listen((_) {
///fetch data
Future<List<dynamic>>.delayed(const Duration(milliseconds: 500),
() async {
return someFunction();
});
});
}
someFunction() {
counter++;
notifyListeners();
}
}
Then you could use it like:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'notifier.dart';
void main() {
final customNotifier = CustomNotifier();
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => customNotifier,
),
//You could add more providers
],
builder: (context, _) {
return const MyApp();
},
),
);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
var customNotifier = Provider.of<CustomNotifier>(
context,
);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'someFunction runs this many times:',
),
Text(
'${customNotifier.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
);
}
}

How to write test for flutter package Flutter WeChat Assets Picker

I am trying to write a test on this flutter package https://pub.dev/packages/wechat_assets_picker using the Mocktail package https://pub.dev/packages/mocktail, but this package does not seem to have test in the documentation.
I have included the minimum reproducible example. The test file is currently not working, it is included as an example test code using the flutter Mocktail package.
It is supposed to mock AssetPicker.pickAssets to test whether it is actually called with the correct arguments. I am running in IOS simulator, we will need to add this key in ios/Runer/Info.plist, otherwise the simulator will close unexpectedly after clicking the add button.
main.dart
import 'package:flutter/material.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: const [],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
final List<AssetEntity>? result =
await AssetPicker.pickAssets(context);
},
tooltip: 'Add photo',
child: const Icon(Icons.add),
),
);
}
}
widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:wechat/main.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
class _MockAssetPicker extends Mock implements AssetPicker {}
void main() {
testWidgets('It should call the WeChat asset picker',
(WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
final BuildContext context = tester.element(find.byType(MyApp));
final assetPickerMock = _MockAssetPicker();
when(() => assetPickerMock.pickAssets(context)).thenAnswer((_) => Future.value([
const AssetEntity(
id: 'id1',
typeInt: 1,
width: 100,
height: 100,
),
]));
AssetPicker.instance = assetPickerMock;
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
verify(() => assetPickerMock.pickAssets(context)).called(1);
});
}
Mocking the picker is supported by the separated picker delegate: https://github.com/fluttercandies/flutter_wechat_assets_picker/pull/315
TL;DR, build your own delegate first, then set it through AssetPicker.setPickerDelegate(TestAssetPickerDelegate());

Provider gives error when calling a widget on the screen

to make it simple , I'm creating a dashboard where I have a Drawer on the left side of the screen and Another widget that will change based on what the user will choose from Drawer.
Anyway , After adding the onPressed for a menu item , when I press to re-call a new widget next to the drawer I'm getting this error :
Could not find the correct provider<MenuController$> above MainScreen Widget.
So first this is my main.dart :
import 'package:admin/constants.dart';
import 'package:admin/controllers/MenuController.dart';
import 'package:admin/screens/dashboard/dashboard_screen.dart';
import 'package:admin/screens/main/main_screen.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Admin Panel',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: bgColor,
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.white),
canvasColor: secondaryColor,
),
home: MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MenuController(),
),
],
child: MainScreen(DashboardScreen()),,
),
);
}
}
and this is my MenuController :
import 'package:flutter/material.dart';
class MenuController extends ChangeNotifier {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
GlobalKey<ScaffoldState> get scaffoldKey => _scaffoldKey;
void controlMenu() {
if (!_scaffoldKey.currentState.isDrawerOpen) {
_scaffoldKey.currentState.openDrawer();
}
}
}
and this is my main_screen :
import 'package:admin/controllers/MenuController.dart';
import 'package:admin/responsive.dart';
import 'package:admin/screens/dashboard/dashboard_screen.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'components/side_menu.dart';
class MainScreen extends StatefulWidget {
Widget newWidget;
MainScreen(this.newWidget);
#override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
key: context.read<MenuController>().scaffoldKey,
drawer: SideMenu(),
body: SafeArea(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// We want this side menu only for large screen
if (Responsive.isDesktop(context))
Expanded(
// default flex = 1
// and it takes 1/6 part of the screen
child: SideMenu(),
),
Expanded(
// It takes 5/6 part of the screen
flex: 5,
child: DashboardScreen(),
),
],
),
),
);
}
}
and finally this is my side_menu :
DrawerListTile(
title: "Fournisseurs",
svgSrc: "assets/icons/menu_tran.svg",
subTitle1: 'Ajouter Un Fournisseur',
subTitle2: 'Liste Des Fournisseurs',
subTitle3: '---------',
press1: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MainScreen(Text('Hello'))),
);
},
After clicking on this in my Drawer I'm getting the error listed above.
As the error states, the correct provider isn't found above the MainScreen Widget.
See if it helps to structure your main.dart like this:
import 'package:admin/constants.dart';
import 'package:admin/controllers/MenuController.dart';
import 'package:admin/screens/dashboard/dashboard_screen.dart';
import 'package:admin/screens/main/main_screen.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MenuController(),
),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Admin Panel',
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: bgColor,
textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
.apply(bodyColor: Colors.white),
canvasColor: secondaryColor,
),
home: MainScreen(DashboardScreen()),
),
);
}
}

Setting theme data for flutter app seems broken

I've tried everything I can think of to change the background color of my flutter app, but every time I run the app, the background is black.
This is main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'auth_controller.dart';
import 'themes/color.dart';
import 'index.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp().then((value) => Get.put(AuthController));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
//ThemeData
title: 'Title',
theme: ThemeData(
brightness: Brightness.light,
),
home:const Index(),
debugShowCheckedModeBanner: false,
);
}
}
I don't think I'm even using themes/color.dart but I thought I'd leave it in anyway. Brightness should set it.
This is index.dart
import 'package:flutter/material.dart';
import 'themes/color.dart';
import 'signup.dart';
Future<void> main() async {
runApp(MyApp(
routes: <String, WidgetBuilder>{
'/signup': (BuildContext context) => const SignUp()
},
debugShowCheckedModeBanner: false,
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key, required Map<String, WidgetBuilder> routes, required bool debugShowCheckedModeBanner}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
backgroundColor: const Color(0xFFe3e4e4),
appBar: AppBar(
title: const Text('Flutter Screen Background Color Example'),
),
body: const Center(child: Index()),
),
);
}
}
class Index extends StatelessWidget {
const Index({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const SizedBox(height: 20,),
ElevatedButton(
child: const Text('WTF'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SignUp()),
);
}
),
]
)
);
}
}
It seems like the background should be a light grey,but it's black. I tried invalidating the caches and restarting too.

Getx onInit not calling

Any reason why app keep stuck on splash screen? I using getx state management. If authToken not empty, it should go to main page. But onInit in controller class not calling.
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: '/splashScreen',
getPages: [
GetPage(name: '/splashScreen', page: () => SplashScreen(),binding: Bind()),
GetPage(
name: '/login', page: () => LoginPage(), binding:Bind()),
GetPage(
name: '/mainPage', page: () => MainPage(), binding:Bind())
],
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
LocalizationDelegate(),
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: [
const Locale('en', ''),
const Locale('zh', ''),
],
title: 'Sample',
theme: ThemeData(
accentIconTheme: const IconThemeData.fallback().copyWith(
color: Colors.white,
),
primaryTextTheme: TextTheme(headline6: TextStyle(color: Colors.orange)),
primarySwatch: white,
primaryIconTheme: const IconThemeData.fallback().copyWith(
color: Colors.white,
),
),
// home: SplashScreen(),
);
}
}
Splash Screen
class SplashScreen extends GetView<MainPageController> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: Container(
height: double.infinity,
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Column(
children: <Widget>[
Container(
height: 250,
width: 250,
child: Image.asset('assets/xxx.png')),
SizedBox(
height: 10,
),
],
)
],
)),
);
}
}
MainPageController
class MainPageController extends GetxController {
final con = Get.find<ProductDefectsController>();
final con1 = Get.find<ProductQualityController>();
final con2 = Get.find<ProductController>();
var tabIndex = 0;
#override
void onInit() async {
print("call onInit"); // this line not printing
// checkIsLogin();
// print("ww");
super.onInit();
}
}
Bind
class Bind extends Bindings {
Repository? _repository;
Bind() {
final _service = Get.put(Service());
final _db = Get.put(Db());
final _dao = Get.put(Dao(_db));
_repository = Get.put(Repository(_dao, _service));
}
#override
void dependencies() {
Get.lazyPut<MainPageController>(() => MainPageController());
Get.lazyPut<LoginPageController>(
() => LoginPageController(_repository!));
}
}
Edit
I tried use middlewares, but I got error
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return GetMaterialApp(
initialRoute: '/splashScreen',
getPages: [
GetPage(
name: '/splashScreen',
page: () => SplashScreen(),
binding: Bind(), middlewares: [GlobalMiddleware()]),
...
],
debugShowCheckedModeBanner: false,
localizationsDelegates: const [
....
],
);
}
}
Middleware
class GlobalMiddleware extends GetMiddleware {
final authService = Get.find<LoginPageController>();
#override
RouteSettings? redirect(String? route) {
print(authService.isLogin.value);
return authService.isLogin.value == true
? RouteSettings(
name: "/mainPage",
)
: RouteSettings(name: "/login");
}
}
Error
"LoginPageController" not found. You need to call "Get.put(LoginPageController())" or "Get.lazyPut(()=>LoginPageController())"
You need to initialize your bindings somewhere before using/finding them. A good place is to provide an instance of your binding class (in your case Bind) to the initialBinding property of GetMaterialApp:
GetMaterialApp(
...
initialBinding: Bind(),
...
)
Then you can use the Middleware approach. But the controller approach may not work just yet because the controller instance isn't used on the page/view.
Remember a controller (or any other dependency) will get initialized (thus calling the lifecycle methods like onInit) only when the controller/dependency is used/called from the page/view. Which isn't happening in your case.
you need to instantiate your controller as in the example below
class HomeBindings implements Bindings {
#override
void dependencies() {
Get.put(HomeController(influencerDataServices: Get.find(), cardsJobsServices: Get.find()));
Get.lazyPut(() => AgendaCardsController(
influencerDataServices: Get.find(), cardsJobsServices: Get.find()));
}
}
then in your route you need to call this binding, like this:
List<GetPage> routers = [
GetPage(
name: "/home",
page: () => const HomePage(),
binding: HomeBindings(),
transition: Transition.leftToRightWithFade,
opaque: false,
showCupertinoParallax: true,
)
];
getx is tightly linked to controllers and bindings, you can also try on the page's on ready instead of on init
You can delete that specific controller when you are going to that controller again by just adding these lines:
Get.delete<Yourcontroller()>();
Get.put(Yourcontroller());
In dependencies, this will solve the problem.
I cant see in this code whether you used Get.put or Get.lazyPut. Make sure that you used one of them before using a Getx controller.