My first Flutter development with GetX. Now encounter a problem.
I have a ListView where the items are all encapsulated Class.
The requirement now is to create an obs List as a data source. The elements in the List are all models.
I now want to pass the model in the List to the item, and click on the item to pass it to the next page for data modification. what should I do?
I am like this
`
Controller:
class FindQADetailController extends GetxController {
var detailEntity = QADetailEntity().obs;
}
Page:
class FindQAPage extends StatefulWidget {
const FindQAPage({Key key}) : super(key: key);
#override
State<FindQAPage> createState() => _FindQAPageState();
}
class _FindQAPageState extends BasePageMixin<FindQAPage, FindQAPresenter>
with SingleTickerProviderStateMixin
implements FindQAIView {
final findQAController = Get.put(FindQAController());
#override
void initState() {
super.initState();
_refresh();
}
#override
Widget build(BuildContext context) {
return RezaAppContainer(
childWidget: Obx(() => DeerListView(
itemCount: findQAController.listData.length,
onRefresh: _refresh,
loadMore: _loadMore,
hasMore: findQAController.hasMore,
itemBuilder: (_, index) {
var entity = findQAController.listData[index];
return FindItemQAPage(entity);
})),
);
}
Item:
class FindItemQAPage extends StatefulWidget {
FindItemQAPage(this.entity, {Key key}) : super(key: key);
QAEntity entity;
#override
State<FindItemQAPage> createState() => _FindItemQAPageState();
}
class _FindItemQAPageState
extends BasePageMixin<FindItemQAPage, FindItemQAPresenter>
with SingleTickerProviderStateMixin
implements FindItemQAIView {
FindItemQAController findItemQAController = Get.put(FindItemQAController());
#override
void initState() {
super.initState();
findItemQAController.entity.value = widget.entity;
}
}
`
I want the elements in the array in the first page to be passed to the item and the next page, and the data modifications made on the next page to be passed to the item in the first page.
Passing Data from 1st Screen to 2nd Screen
You can pass any data using arguments parameters in navigation methods
Get.to(ScreenName(),arguments: PASS_DATA);
If you are doing navigation with routes still you can pass data
Get.toNamed(RoutesName, arguments: PASS_DATA);
For navigate data back from 2nd screen to 1st screen you can user result property.
Get.back(result:PASS_DATA);
Pass data from 1st screen to 2nd screen & vice versa.
1st screen controller
import 'package:get/get.dart';
class FirstController extends GetxController {
/// Navigation method.
Future<void> btnNavigateTap() async {
//.... Using Get.toNamed method || use this if you are using routeName
//.... Pass Data from 1st screen to 2nd screen
Get.toNamed(
SeconScreenRouteName,
arguments: {
"user": "Jems",
"emails": ["abc#gmail.com", "pqr#gmail.com"],
},
);
//.... Using Get.to method
//.... Pass Data from 1st screen to 2nd screen
Get.to(
SecondScreen(),
arguments = {
"user": "Jems",
"emails": ["abc#gmail.com", "pqr#gmail.com"],
},
);
//.... Get Data from 2nd screen to 1st screen
final result = await Get.to(
SecondScreen(),
arguments = {
"user": "Jems",
"emails": ["abc#gmail.com", "pqr#gmail.com"],
},
);
}
}
2nd screen controller to access data.
import 'package:get/get.dart';
class SecondController extends GetxController {
late String user;
late List<String> emails;
dynamic argumentData;
#override
void onInit() {
super.onInit();
user = argumentData['user'];
emails = argumentData['emails'];
}
void btnBackTap(){
//... Passing data to previous screen.
Get.back(result:{
"user":"Jack",
});
}
}
For more details about navigation & parsing data using getX check this reference link
Related
I'm new to GetX and am trying to migrate a project from Provider. I am using multi_split_view in my app to have resizable panes, and I set it up in a StatefulWidget like this:
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<PilotProApp> {
final MultiSplitViewController _splitViewController = MultiSplitViewController();
#override
Widget build(BuildContext context) {
//var menu = Provider.of<ModelMenu>(context); //<-- Old provider variable
final menu = Get.put(ControllerMenu()).menu; //<-- New GetX observable
//Update splitView areas for Dashboard
_splitViewController.areas = menu.value == 'Dashboard'
? [Area(weight: .75), Area(weight: .25)]
: [Area(weight: .25), Area(weight: .75)];
MultiSplitView splitView = MultiSplitView(
controller: _splitViewController,
children: const [
Master(),
Detail(),
],
);
MultiSplitViewTheme splitViewTheme = MultiSplitViewTheme(
data: MultiSplitViewThemeData(dividerThickness: 16),
child: splitView,
);
}
}
I know that I have to wrap a widget in Obx(()=>) in order to make it reactive, but the place I need reactivity here is where it says //Update splitView areas for Dashboard. In Provider, when the menu variable changed, it would reset the _splitViewController.areas.
But with GetX, I'm unclear on how to make a configuration change like that reactive. Where should Obx(()=>) go in this case? Or do I need to move my entire MultiSplitViewController into my ControllerMenu somehow?
Well that didn't take long to figure out. Let me know if I'm wrong on this, but I think I can make my widget a StatelessWidget and put my MultiSplitViewController in my controller like this:
class ControllerMenu extends GetxController {
final menu = 'Dashboard'.obs;
final splitViewController = MultiSplitViewController();
#override
void onInit() {
super.onInit();
//Watch for changes on `menu`
ever(
menu,
(value) {
//Update controller
if (value == 'Dashboard') {
splitViewController.areas = [Area(weight: .75), Area(weight: .25)];
} else {
splitViewController.areas = [Area(weight: .25), Area(weight: .75)];
}
},
);
}
}
Then I just plug in my MultiSplitViewController back into my view:
Widget build(BuildContext context) {
final splitViewController = Get.put(ControllerMenu()).splitViewController;
MultiSplitView splitView = MultiSplitView(
controller: splitViewController,
children: const [
Master(),
Detail(),
],
);
}
Seems to work great!
I am new to the Flutter GetX package and facing problem when using the flutter GetX package. I have several app screens where one screen for listing all the products from the database.
In my product controller, I am fetching all the data from the database and showing with listview like as given code below and It's working fine.
Problem: When I'm inserting a new record from another controller and come back to the product list controller it'll not showing newly added data. Actually this time onInit method won't fire.
Controller code
class ProductIndexCtrl extends GetxController {
var products = [].obs;
#override
void onInit() {
super.onInit();
getAll();
}
void getAll() {
Product.getAll().then((jsonList) {
products.value = Product.fromJsonList(jsonList);
});
}
}
class ProductCreateCtrl extends GetxController {
void saveData(Product product) {
...
...
//after successful insert
Get.toNamed('productIndex');
}
}
Product index screen
final ctrl = Get.put(ProductIndexCtrl());
GetX<ProductIndexCtrl>(builder: (controller) {
return _listview(controller.products);
}
Widget _listview(data) {
...
...
}
As the GetX dependency manager controls the lifecycle of dependencies, you should not manually call them. It's GetX's responsibility when to call them.
Therefore you need to manually call getAll() method of your ProductIndexCtrl controller inside the saveData() method of your ProductCreateCtrl like:
saveData(Product product){
....
....
final indexCtrl= Get.find<ProductIndexCtrl>();
indexCtrl.getAll();
}
By returning to that page, you can return the new information locally to the previous page
> Controller code
class ProductIndexCtrl extends GetxController {
var products = [].obs;
#override
void onInit() {
super.onInit();
getAll();
}
void getAll() {
Product.getAll().then((jsonList) {
products.value = Product.fromJsonList(jsonList);
});
}
}
> Product index screen
class ProductCreateCtrl extends GetxController {
void saveData(Product product) {
...
...
//after successful insert
Get.back(result: product);
}
}
and get Data when back
Get.toName('ProductCreateCtrl').then(result){
products.add(result);
}
I tried a similar thing with one single controller. The code snippet is given below.
First, create the ProductView. Since this is the entry point of the application, So you will create a GetX controller inside of this.
/// THIS IS PARENT VIEW SO WE WILL CREATE GETX CONTROLLER HERE
class ProductView extends StatelessWidget {
const ProductView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final controller = Get.put(ProductController());
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: Obx(() {
return controller.myProductList.isEmpty
? showNoProductView()
: ListView.builder(
itemCount: controller.myProductList.length,
itemBuilder: (context, index) {
return YourListItemView(controller.myProductList[index]);
},
);
}),
);
}
}
The view AddProductView is responsible for adding new products to the DB. We can assume that there is a FloatingActionButton present in ProductView and onClick on that button, we will open this AddProductView.
/// THIS IS CHILD VIEW SO WE WILL FIND THE PRODUCT CONTROLLER HERE
class AddProductView extends StatelessWidget {
const AddProductView({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final controller = Get.find<ProductController>();
return Scaffold(
appBar: AppBar(
title: const Text('Add Product'),
),
body: Column(
children: [
// ADD YOUR OTHER WIDGETS TO GET PRODUCT INFO
TextButton(
child: const Text('Click to Add'),
onPressed: () {
var productName = nameTextEditingController.text;
var productQuantity = qtyTextEditingController.text;
var product = YourProductObject(productName, productQuantity);
controller.addProduct(product: product);
},
)
],
),
);
}
}
Finally, the controller will look like this.
import 'package:get/get.dart';
class ProductController extends GetxController {
// this will be your custom product list object
var myProductList = <YourProductObject>[].obs;
var dbInstance = YourDbInstance();
#override
void onReady() async {
super.onReady();
// perform database operation
await fetchDataFromDb();
}
Future<void> fetchDataFromDb() async {
// assuming that data is coming as List<YourProductListObject>
// always use try catch in db operation. for demo purpose I am skipping that.
var productListFromDb = await dbInstance.getYourProductListObjectList();
myProductList.assignAll(productListFromDb);
}
Future<void> addProduct({required YourProductObject product}) async {
// assuming that there is a function that returns true if a product is added to db
var isAdded = await dbInstance.addProduct(product);
if (isAdded) {
myProductList.add(product);
}
}
}
Since myProductList is a RxList so getx will observe it and will update the UI accordingly. You must add Obx((){}) in view.
I have two page and I want to use the variable 'id' in the second screen to fetch data from API.
What should I do?
Screen one: it's the product screen where user click on profile image and after that I get all information about user owner in the second screen.
Screen two: I display data for this user by id
NB: I get all the data by API
id is always Null
Screen one:
InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfile(
id: id,
)),
);
// do something here
},
),
Screen two:
class UserProfile extends StatefulWidget {
final int id;
const UserProfile({Key key, #required this.id}) : super(key: key);
#override
_UserProfileState createState() => _UserProfileState();
}
class _UserProfileState extends State<UserProfile> {
#override
void initState() {
getprofile(id);
super.initState();
}
Future<List<dynamic>> getprofile(int id) async {
var response = await Network().getData('/auth/user/$id');
data = json.decode(response.body);
return data;
}
When you want to use a property from the StatefulWidget you need to use widget.propertyName. In your case it's widget.id
class _UserProfileState extends State<UserProfile> {
#override
void initState() {
getprofile(widget.id);
super.initState();
}
Future<List<dynamic>> getprofile(int id) async {
var response = await Network().getData('/auth/user/$id');
data = json.decode(response.body);
return data;
}
Either do the same that you did before,so pass the id as a parameter to the _UserProfileState class, so just call:
_UserProfileState(#required this.id) : super();
Another option to make variables available is to use the Provider widget
i want to know how to pass data from one class to another class.
Main CLASS
void WhenPageScroll() {
int USERID = int.parse(complexTutorial.data[_pageController.page.toInt()].userId);
print("ID $USERID");
Tut(int.parse(complexTutorial.data[_pageController.page.toInt()].userId));
}
Tut Class
class Tut extends StatefulWidget {
Tut(this.USERID);
final int USERID;
#override
_TutState createState() => _TutState();
}
class _TutState extends State<Tut>{
#override
Widget build(BuildContext context) {
return Container(child: LoadTut(),);
}
LoadTut(){
return Center(child: Text("CENTER TEXT WITH " + widget.USERID.toString()));
}
}
i am using this method but no working how can i do that on page Scrolled?
Note : when i am using setState then whole page are reload but i want to reload only Tut on Scrolled.
Thanks Team.
I'm creating a bottom navigation bar and everything works fine except when the user presses the back button on their device. The state of the navigation bar doesn't update to reflect the page they're on. To fix this, I found out about NavigatorObserver. Now I can see when a route gets popped but I have no way of updating the state. My navigation bar uses routes so when a user taps a button, it'll push a new route. I'm trying to update the index of the navigation bar but I can't see a way to do so. The navbar is using a StatefulWidget so I can use the setState callback.
I've tried using keys but I can't re-use them since they're on different Scaffolds. I can't access the BuildContext from a NavigationObserver so I can't use things like Provider to notify a change and to rebuild.
class CustomBottomNavBar extends StatefulWidget {
#override
_CustomBottomNavBarState createState() => _CustomBottomNavBarState();
}
class _CustomBottomNavBarState extends State<CustomBottomNavBar> {
static int _selectedIndex;
int get selectedIndex => _selectedIndex;
set selectedIndex(int newIndex) {
setState(() {
_selectedIndex = newIndex;
});
}
//...
}
class NavBarObserver extends NavigatorObserver {
#override
void didPop(Route route, Route previousRoute) {
final navBarRoutes = Routes.all.sublist(0,4);
final routeName = route.settings.name;
if (navBarRoutes.contains(routeName)) {
final index = navBarRoutes.indexOf(routeName);
// selectedIndex = index;
}
}
}
You can do it couple ways. Here I have given an example with ValueNotifier. First you can create a enum that define different pages of you bottom navigation bar. Then you create a ValueNotifier with your enum type and share it between NavBarObserver and CustomBottomNavBar. In NavBarObserver when any tab change occurred you simply updated the ValueNotifier value with corresponding tab's enum value. You can listen ValueNotifier value change in _CustomBottomNavBarState to update the bottom navigation bar state.
enum Tabs{one, two, three}
class CustomBottomNavBar extends StatefulWidget {
final ValueNotifier<Tabs> tabsChangeNotifier;
CustomBottomNavBar(this.tabsChangeNotifier);
#override
_CustomBottomNavBarState createState() => _CustomBottomNavBarState();
}
class _CustomBottomNavBarState extends State<CustomBottomNavBar> {
Tabs _currentTab;
#override
void initState() {
super.initState();
widget.tabsChangeNotifier.addListener(() {
setState(() {
_currentTab = widget.tabsChangeNotifier.value;
});
});
}
}
class NavBarObserver extends NavigatorObserver {
final ValueNotifier<Tabs> tabsChangeNotifier;
NavBarObserver(this.tabsChangeNotifier);
#override
void didPop(Route route, Route previousRoute) {
final navBarRoutes = Routes.all.sublist(0,4);
final routeName = route.settings.name;
if (navBarRoutes.contains(routeName)) {
tabsChangeNotifier.value = Tabs.two;
// selectedIndex = index;
}
}
}