Created
December 23, 2020 14:59
-
-
Save yasinarik/2dfdf932c784897d51215bef543ecd55 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ATTENTION! | |
//You have to add these to pubspec.yaml: | |
//get: ^3.24.0 | |
//provider: ^4.3.2+3 | |
//uuid: ^2.2.2 | |
import 'package:flutter/material.dart'; | |
import 'package:get/get.dart'; | |
import 'package:provider/provider.dart'; | |
import 'package:uuid/uuid.dart'; | |
/* -------------------------------------------------------------------------- */ | |
/* main */ | |
/* -------------------------------------------------------------------------- */ | |
void main() async { | |
runApp(App()); | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* App */ | |
/* -------------------------------------------------------------------------- */ | |
class App extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return GetMaterialApp( | |
home: MainView(), | |
); | |
} | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* TagPVD */ | |
/* -------------------------------------------------------------------------- */ | |
class TagPVD with ChangeNotifier { | |
TagPVD() { | |
init(); | |
} | |
/// Generates a random uuid on init. | |
init() => tag = Uuid().v1(); | |
String tag = ""; | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* InstanceSeparator */ | |
/* -------------------------------------------------------------------------- */ | |
/// InstanceSeparator basically creates a new pair of ChangeNotifierProvider-Consumer | |
/// every time a new instance of MainView is created in the widget tree. | |
/// This new pair uses the TagPVD (TagProvider, shortened). | |
/// | |
/// TagPVD has an init method inside of its constructor. It basically generates a new Uuid String and set as the tag variable. | |
/// So, every children of the InstanceSeparator widget have access to the TagPVD respectively. | |
class InstanceSeparator extends StatelessWidget { | |
final Function(String uniqueTag) builder; | |
InstanceSeparator({ | |
Key key, | |
@required this.builder, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return ChangeNotifierProvider<TagPVD>( | |
create: (context) => TagPVD(), | |
child: Consumer<TagPVD>( | |
builder: (context, tagPVD, _) { | |
return builder(tagPVD.tag); // This is the builder property of InstanceSeparator. Not related to provider. We will use this | |
}, | |
), | |
); | |
} | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* MainView */ | |
/* -------------------------------------------------------------------------- */ | |
class MainView extends StatelessWidget { | |
/// The "controller" variable is defined here. But haven't set it to anything yet. DON'T make it final. | |
/// Why here? Because I want to access "controller" variable inside custom methods of this MainView class. | |
/// I generally need some class methods. They improve readability too. | |
/// For ex: buildList(), buildHeader(), buildProfilePicture() | |
/// If I didn't define the "controller" variable here, then I would have to pass it to the methods(functions). | |
/// For ex: buildList(controller), buildHeader(controller), buildProfilePicture(controller) | |
Controller controller; | |
@override | |
Widget build(BuildContext context) { | |
/// Instance separator wraps all of the children of MainView. | |
return InstanceSeparator( | |
builder: (uniqueTag) { | |
/// the "controller" variables are set inside here. We can grab the "uniqueTag". | |
/// This "uniqueTag" parameter is shared down to the widget tree. | |
/// Child widgets can look for this parameter up in the tree because the provider indeed an InheritedWidget. | |
controller = Get.put(Controller(), tag: uniqueTag); | |
return Obx(() { | |
return Scaffold( | |
body: Center( | |
child: Column( | |
children: [ | |
// Title | |
Container( | |
padding: EdgeInsets.all(16), | |
margin: EdgeInsets.all(16), | |
color: Colors.red, | |
child: Text("Example usage of GetX / Obx with a \"tag\" property."), | |
), | |
/// Button | |
/// Navigating to a new screen, in our case a new MainView. | |
/// So the new MainView will have a separate instance of Get controller. | |
/// Also, all of its children will share the same controller with the help of InstanceSeparator. | |
GestureDetector( | |
onTap: () { | |
Get.to(MainView(), preventDuplicates: false); | |
}, | |
child: Container( | |
color: Colors.pink, | |
margin: EdgeInsets.all(16), | |
padding: EdgeInsets.all(16), | |
child: Text("TAP --> Navigate to a new MainView"), | |
), | |
), | |
// Button | |
GestureDetector( | |
onTap: () { | |
controller.addItemToList(); | |
}, | |
child: Container( | |
color: Colors.blue, | |
margin: EdgeInsets.all(16), | |
padding: EdgeInsets.all(16), | |
child: Text("TAP --> Add item to list"), | |
), | |
), | |
// ListView with builder | |
Container( | |
height: 200, | |
child: ListView.builder( | |
scrollDirection: Axis.horizontal, | |
itemCount: controller.itemList.length, | |
itemBuilder: (_, i) { | |
return ListItemWidget(index: i); | |
}, | |
), | |
), | |
// ListView with a class method. Just for demonstration. | |
Container( | |
height: 200, | |
child: ListView( | |
scrollDirection: Axis.horizontal, | |
children: buildListView(), | |
), | |
), | |
], | |
), | |
), | |
); | |
}); | |
}, | |
); | |
} | |
/// Obviously, this method is not needed. It is here just because of demonstration purposes. | |
/// Here we use the "controller" without calling it as a parameter inside --> buildList() method. | |
/// Because at the beginning of the MainView class, we already defined it as --> Controller controller; | |
/// It might be confusing but if you use custom methods inside a widget class like me, | |
/// it is better to define them first without setting to anything. | |
List<Widget> buildListView() { | |
List<Widget> wl = []; | |
Widget w; | |
/// Reminder: We set the "controller" variable inside --> InstanceSeparator --> controller = Get.put(Controller(), tag: uniqueTag); | |
for (var i = 0; i < controller.itemList.length; i++) { | |
w = ListItemWidget(index: i); | |
wl.add(w); | |
} | |
return wl; | |
} | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* ListItemWidget */ | |
/* -------------------------------------------------------------------------- */ | |
class ListItemWidget extends StatelessWidget { | |
final int index; | |
ListItemWidget({ | |
this.index, | |
}); | |
/// Again, in order to make "controller" and "tagPVD" variables available inside custom class methods, | |
/// we define them here. (not final). We will set them later. | |
Controller controller; | |
TagPVD tagPVD; | |
@override | |
Widget build(BuildContext context) { | |
/// As a restriction of Provider package, we have to use "context". | |
/// So I have to set "tagPVD" just here inside main build() method. | |
/// On the other hand, Provider let us access to the "RIGHT" instance just by looking for the tree above. | |
tagPVD = Provider.of<TagPVD>(context); | |
/// Since we now have "tagPVD", we can get the "tag" property of it. Which is the RIGHT String for us to use. | |
controller = Get.find(tag: tagPVD.tag); | |
/// The rest is the same as regular usage of Obx(()=>Widget()); | |
return Obx(() { | |
return GestureDetector( | |
behavior: HitTestBehavior.translucent, | |
onTap: () { | |
controller.increaseCounterAtIndex(index); | |
}, | |
child: Container( | |
margin: EdgeInsets.all(16), | |
height: 200, | |
color: Colors.yellow, | |
child: Column( | |
children: [ | |
Container( | |
padding: EdgeInsets.all(12), | |
child: Text("This is a ListItemWidget with index: $index"), | |
), | |
buildCounterSection(), | |
Container( | |
padding: EdgeInsets.all(12), | |
child: Text("TAP to increase this counter"), | |
), | |
], | |
), | |
), | |
); | |
}); | |
} | |
/// Let's use a method. We don't have to pass the "controller". --> Widget buildCounterSection(Controller controller) {} | |
Widget buildCounterSection() { | |
return Container( | |
padding: EdgeInsets.all(12), | |
child: Text("Counter of this item: " + controller.itemList[index].value.toString()), | |
); | |
} | |
} | |
/* -------------------------------------------------------------------------- */ | |
/* Controller */ | |
/* -------------------------------------------------------------------------- */ | |
class Controller extends GetxController { | |
var itemList = <RxInt>[].obs; | |
addItemToList() { | |
itemList.add(0.obs); | |
} | |
increaseCounterAtIndex(int index) { | |
itemList[index].value++; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment