I want to use Wrap on large amount of objects.
I tried to just map all object to children but it cause serious performance issue.
I want some alternative way to build only currently displayed widgets but with style of Wrap.
Some code:
Wrap(
children: list.map(createCardFromData), // List contains 20'000 items
);
This is really good example flutter is missing in my opinion.
This is also something which is being discussed on Flutter's repo
https://github.com/flutter/flutter/issues/97544
Unfortunately, it will take some time for it to be in stable release. For the time being, I would suggest to paginate the data into chunks of maybe 100 items. There are other ways as well which might involve a lot of calculations. Meanwhile, I (or maybe some other person) could try to come up with a efficient solution and maybe contribute to the Flutter.
After some fiddling, I could build sample app per your requirement.
pubspec.yaml
name: scrollable_wrap
description: A new Flutter project.
publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.18.4 <3.0.0"
dependencies:
cupertino_icons: ^1.0.2
flutter:
sdk: flutter
flutter_svg: ^1.1.6
random_avatar: ^0.0.7
random_words: ^1.0.2
dynamic_layouts:
git:
url: git#github.com:flutter/packages.git
path: packages/dynamic_layouts
dev_dependencies:
flutter_lints: ^2.0.0
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
main.dart
import 'package:dynamic_layouts/dynamic_layouts.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:random_avatar/random_avatar.dart';
import 'package:random_words/random_words.dart';
class Item {
final String label;
final String avatar;
Item(this.label) : avatar = randomAvatarString(label);
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Scrollable Wrap',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<Item> data = [];
int n = 100000;
void init() async {
data = generateNoun().take(n).map((e) => Item(e.asString)).toList();
setState(() {});
}
#override
void initState() {
super.initState();
init();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Scrollable Wrap'),
),
body: CustomScrollView(slivers: [
DynamicSliverGrid(
gridDelegate: const SliverGridDelegateWithWrapping(
mainAxisSpacing: 0,
crossAxisSpacing: 0,
childCrossAxisExtent: double.infinity,
childMainAxisExtent: double.infinity,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
if (kDebugMode) {
print('build called for $index');
}
final item = data[index];
return Chip(
key: ValueKey(item),
label: Text('$index ${item.label}'),
avatar: SvgPicture.string(item.avatar),
);
},
childCount: data.length,
),
),
]),
);
}
}
Output
Please use flutter run --no-sound-null-safety as one of the library is not null-safe. Also, you might find lag as lots of svgs are being processed on scroll. It might not happen in production.
Do you need this many items loaded at once? You're probably using a scroll view anyway, so the user only initially sees a small batch of items rather than all of them, until they actually scroll for more. What you're looking for is lazy loading, maybe combined with a technique like infinite scroll.
Try a ListView (specifically, ListView.builder) where each item contains a Wrap widget (say, each 10 items - but you may want to experiment with this number until you see a balance between performance and visual appeal).
Or, alternatively, you may code your own Wrap that does loading lazily and reuses its views, so that it only loads and displays a couple of its children as needed, not thousands at once.
If your items are fixed width you could try something like this:
import 'dart:math';
import 'package:flutter/cupertino.dart';
typedef ValueWidgetBuilder<T> = Widget Function(T value);
class WrapBuilder extends StatelessWidget {
final double itemWidth;
final List items;
final ValueWidgetBuilder itemBuilder;
const WrapBuilder(
{Key? key,
required this.itemWidth,
required this.items,
required this.itemBuilder})
: super(key: key);
#override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
var cardsPerRow = max(1, constraints.maxWidth ~/ itemWidth);
return ListView.builder(
shrinkWrap: true,
controller: ScrollController(),
itemCount: (items.length / cardsPerRow).ceil(),
itemBuilder: (BuildContext context, int index) {
var rowItems = items.sublist(cardsPerRow * index,
min(cardsPerRow * (index + 1), items.length));
return Row(children: [
for (final item in rowItems)
SizedBox(
width: itemWidth,
child: itemBuilder(item))
]);
},
);
});
}
}
And then use like
WrapBuilder(
itemWidth: 100, //example
items: list,
itemBuilder: createCardFromData);
Related
Get the working code sample here
I have an RxList of addOnProducts which contains product and selected attributes.
I am trying to implement the simple multiSelectable grid View, but on clicking the checkBox the selected attribute changes but it is not reflected back to the ui,
If i refresh it will be updated.
I tried Obx()=> (); widget , It is still not updating
My ProductController
class ProductsController extends GetxController {
late Worker worker;
static ProductsController instance = Get.find();
RxList<ProductModel> products = RxList<ProductModel>([]);
RxList<CheckProduct> addOnProducts = <CheckProduct>[].obs;
String collection = "products";
#override
void onReady() {
super.onReady();
products.bindStream(getAllProducts());
worker = once(products, (List<ProductModel> value) {
fillAddOnProducts(value);
}, condition: () => products.isNotEmpty);
}
Stream<List<ProductModel>> getAllProducts() => FirebaseFirestore.instance
.collection(collection)
.snapshots()
.map((query) => query.docs
.map((item) => ProductModel.fromMap(item.data(), item.id))
.toList());
void fillAddOnProducts(List<ProductModel> products) => {
products.forEach((element) {
addOnProducts.add(CheckProduct(product: element, selected: false));
})
};
}
class CheckProduct {
ProductModel product;
bool selected;
CheckProduct(
{required ProductModel this.product, required bool this.selected});
}
My Grid View
class AddOns extends StatelessWidget {
const AddOns({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [],
title: Text("Select Addons"),
),
body: Obx(() => GridView.count(
crossAxisCount: 2,
children: productsController.addOnProducts
.map((element) => ProductWidget(product: element))
.toList(),
)));
}
}
class ProductWidget extends StatelessWidget {
final CheckProduct product;
const ProductWidget({Key? key, required this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
margin: EdgeInsets.all(10),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 4,
left: 4,
child: Checkbox(
value: product.selected,
onChanged: (value) {
print("value of the value is : $value");
print("value of product selected before is: " +
product.selected.toString());
product.selected = value!;
print("value of product selected after is: " +
product.selected.toString());
},
),
),
],
));
}
}
Therefore in the console it is :
I/flutter (20067): value of the value is : true
I/flutter (20067): value of product selected before is: false
I/flutter (20067): value of product selected after is: true
But the checkBox is not updating, it updates only when i refresh, How to overCome this? Adding Obx() to the parent isn't helping..
Find the github link to code below here which has just the question and and the problem faced..
After going through your code. I've implemented the following that will change state without hot reload:
In your main dart you do not need to put your product controller here as you are not using it
main.dart
import 'package:flutter/material.dart';
import 'grid.dart';
void main() {
runApp(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: GridSelect(),
);
}
}
Next, I have changed your grid class to generate a list of product widget as the size of the addProduct list length. In my opinion this is a better way to write GridView counts children. Remove obx from your gridview and change your stateful widget to stateless as you are using Getx. It will manage your state even in a stateless widget. Add your product controller here as you will access addProduct list from the controller class.
grid.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:test_project/controllers/productController.dart';
import 'package:test_project/productWidget.dart';
class GridSelect extends StatelessWidget {
final _controller = Get.put(ProductController());
GridSelect({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: GridView.count(
crossAxisCount: 2,
children: List.generate(_controller.addOnProducts.length, (index) => ProductWidget(index: index))
),
);
}
}
In your product controller class, remove the instance as it is not important. That is the only change here:
ProductController.dart
import 'package:get/get.dart';
import 'package:test_project/models/productModel.dart';
class ProductController extends GetxController {
RxList<CheckProduct> addOnProducts = <CheckProduct>[].obs;
#override
void onReady() {
super.onReady();
addOnProducts.add(CheckProduct(product: ProductModel('productOne', 20)));
addOnProducts.add(CheckProduct(product: ProductModel('productTwo', 25)));
addOnProducts.add(CheckProduct(product: ProductModel('productThree', 30)));
addOnProducts.add(CheckProduct(product: ProductModel('productFour', 40)));
}
}
class CheckProduct {
ProductModel product;
RxBool selected = false.obs;
CheckProduct({
required this.product,
});
}
Lastly, your productWidget class needs a required value index. So, the widget knows which index in gridview the user is clicking and use Obx() here in checkbox as you have an observable value selected here. Remember to always use Obx() when you have an obs value. This will update the widget whenever an obs value changes. Here, if you notice we are using Get.find() instead of Put as Get.put is already inside the scope so all you need to do is find the controller that you will use. You can find or put multiple controllers and update values as much as you want.
productWidget.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:test_project/controllers/productController.dart';
class ProductWidget extends StatelessWidget {
final ProductController _controller = Get.find();
final int index;
ProductWidget({Key? key, required this.index}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
margin: EdgeInsets.all(20),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 4,
left: 4,
child: Obx(()=>Checkbox(
value: _controller.addOnProducts[index].selected.value,
onChanged: (value) {
print("value of the value is : $value");
print("value of product selected before is: " +
_controller.addOnProducts[index].selected.toString());
_controller.addOnProducts[index].selected.value = value!;
print("value of product selected after is: " +
_controller.addOnProducts[index].selected.toString());
},
)),
)
],
),
);
}
}
Go through GetX documentation for proper use of GetX. Even though I have 2 apps in Playstore with GetX, I still go through documentation from time to time. They have a clear documentation on how to manage state.
In ProductWidget adding an additional Obx() solved my problem
class ProductWidget extends StatelessWidget {
final CheckProduct product;
const ProductWidget({Key? key, required this.product}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
margin: EdgeInsets.all(10),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(
top: 4,
left: 4,
// Even the child needs Obx() ; The parent's Obx() is not reflected here
child: Obx(()=>(Checkbox(
value: product.selected,
onChanged: (value) {
print("value of the value is : $value");
print("value of product selected before is: " +
product.selected.toString());
product.selected = value!;
print("value of product selected after is: " +
product.selected.toString());
},
),))
),
],
));
}
i have problem in Dismissible Widget. when you run this code :
import 'dart:math';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('appbar'),
),
body: const MyStatefulWidgetState(),
),
);
}
}
class MyStatefulWidgetState extends StatefulWidget {
const MyStatefulWidgetState({Key? key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidgetState> {
final List<String> items = List<String>.generate(10, (i) => "Item ${i + 1}");
#override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(
height: 2,
),
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Dismissible(
key: Key(item),
onDismissed: (DismissDirection direction) {
//Remove the item from the data source.
setState(() {
items.removeAt(index);
});
},
child: Container(
height: 90,
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Text(item),
),
color: UniqueColorGenerator.getColor(),
),
);
},
);
}
}
class UniqueColorGenerator {
static Random random = Random();
static Color getColor() {
return Color.fromARGB(
255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
}
and when you Dismiss a container, after Container removed from list, all the container's color changed ( like reset the app or hot reload and get new color )
1 - why this happened?
2 - how can i fix this? so when i Dismiss container, the color of other container remain the same color?
thanks
I am not too far into the optimizations of Flutter with keys so I am not sure why your key doesn't do anything but the reason this happens by my understanding is the setState call.
setState will cause the build function to be run again which in turn means that the itemBuilder function will have to be run again which means that you are generating a new color for your container each time the itemBuilder function runs (eventhough it's for the same item) which explains the behaviour you are seeing.
Now for changing that, you can go different ways, two of which are described here:
Option 1 - Generate color based on the item
The Random constructor takes an optional seed allowing you to reproduce a given set of random values each time. If you use the item's hashCode, you will still generate an effectively random color per item:
class UniqueColorGenerator {
static Color getColor(String item) {
final random = Random(item.hashCode);
return Color.fromARGB(
255, random.nextInt(255), random.nextInt(255), random.nextInt(255));
}
}
then use that for your Container color with color: UniqueColorGenerator.getColor(item),. The advantage of this approach is that (at least with Strings as item) the colors are consistent even between restarts. For more complex objects, make sure you override the hashCode getter!
Option 2 - Make the colors part of the item state
To ensure you don't lose the information on which item had what colors, store it together with the items.
First, create an Item class to hold our data neatly together:
class Item {
final String text;
final Color color;
Item(this.text, this.color);
}
Next up, update the generation of the items to make use of that class:
final List<Item> items = List<Item>.generate(
10,
(i) => Item("Item ${i + 1}", UniqueColorGenerator.getColor())
);
And lastly, update the usages of it (so the Container's color becomes item.color, and what was previously item is now item.text).
With this approach, the colors won't be retained throughout restarts.
i have ListView.builder that display data from server in stream from older to latest date dates . that's mean New data always comes to the top of the ListView.builder and while ListView.builder receiving new item it resize its self so the scroll move go up step by step. how to prevent that ? ... ok i have image here
i need number 5 to happen.
of course i can make the property reverse to true . ok this will solve one side only and the same Scenario will happen from down the page
ListView.builder(
itemBuilder: myList.length,
itemBuilder: (BuildContext context, int index) {
return myList['item']
},
),
any help guys most welcome . thanks
First of all, some background on why the behavior you want to avoid happens:
Flutter does not remember to which item the user has scrolled. Instead, it stores how far the user already scrolled, thus when rebuilding, the old scroll offset will correspond to a different item as some items have been inserted in the list prior to the one visible at a specific scroll offset. Here's a possible fix: You can give the ListView.builder a ScrollController. Using the ScrollController you can increase the current scroll offset by the amount of space, the new, at the top of the list inserted items occupy, like so: controller.jumpTo(controller.offset + tileHeight*(newLength - _lastLength));
Here is a small code example in which I implemented it:
import 'package:flutter/material.dart';
void main() {
runApp(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: 'Test',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
ScrollController controller = ScrollController();
final tileHeight = 50.0;
int _lastLength = -1;
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<List<String>>(
stream: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
final newLength = snapshot.data!.length;
if(newLength > _lastLength && _lastLength > 0){
controller.jumpTo(controller.offset + tileHeight*(newLength - _lastLength));
}
_lastLength = newLength;
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return SizedBox(
height: tileHeight,
child: ListTile(
title: Text(
snapshot.data![index],
),
),
);
},
controller: controller,
);
}
return const CircularProgressIndicator();
},
),
);
}
Stream<List<String>> getData() async* {
yield List.generate(20, (index) => index.toString());
await Future.delayed(const Duration(seconds: 5));
yield ["-1"] + List.generate(20, (index) => index.toString());
}
}
I can not get it done to access a simple property of a stateless widget from another widget...
Class Month:
class Month {
final String name;
const Month(this.name);
}
MonthCardWidget:
class MonthCardWidget extends StatelessWidget {
final Month month;
const MonthCardWidget({Key key, this.month}) : super(key: key);
...
in my Stateful widget:
final List months = [
MonthCardWidget(month: Month('January')),
MonthCardWidget(month: Month('February')),
MonthCardWidget(month: Month('March')),
MonthCardWidget(month: Month('April')),
MonthCardWidget(month: Month('May')),
MonthCardWidget(month: Month('Juni')),
MonthCardWidget(month: Month('Juli')),
MonthCardWidget(month: Month('August')),
MonthCardWidget(month: Month('September')),
MonthCardWidget(month: Month('October')),
MonthCardWidget(month: Month('November')),
MonthCardWidget(month: Month('December')),
];
and I try to access the name like this:
months[index].month.name
but this is not working... What am I missing here?
Would it not be easier to make a list of Month models instead of widgets?
I think what you want is a state management solution. For this you can use packages like provider or GetX. My personal favorite is the GetX Package. You just need to create a class where you put your lists,variables,... you want to access globally and then call them with a controller.
For a detailed documentation check out their package on pub.dev
https://pub.dev/packages/get
Good Luck! :)
It's probably a bad idea to access the properties of the Widget from its ancestor. The data flows in the other direction. Using State Management is a great way to manage/share your data around your Widget Tree. Check this curated list of State Management Packages. My personal favorite is Riverpod (a new package from the same author as the Provider package).
For your particular case, I'm not even sure you need a Month class and a List<MonthCardWidget> months. Months are just numbers from 1 to 12.
The children of your wheel can be generated as a List:
ClickableListWheelScrollView(
[...]
child: ListWheelScrollView(
[...]
children: List.generate(12, (index) => MonthCardWidget(month: index + 1)),
),
);
And then, your MonthCardWidget just receives an int month and displays the month name thanks to the intl package, giving you i18n for free:
class MonthCardWidget extends StatelessWidget {
final int month;
const MonthCardWidget({Key key, this.month}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.amber.shade300,
child: Center(
child: Text(DateFormat.MMMM().format(DateTime(2000, month))),
),
);
}
}
Full source code
import 'package:clickable_list_wheel_view/clickable_list_wheel_widget.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Calendar Wheel Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: MyListWheel(),
);
}
}
class MyListWheel extends StatefulWidget {
#override
_MyListWheelState createState() => _MyListWheelState();
}
class _MyListWheelState extends State {
final _scrollController = FixedExtentScrollController(initialItem: 11);
#override
Widget build(BuildContext context) {
final double _itemHeight = MediaQuery.of(context).size.height / 3;
return ClickableListWheelScrollView(
scrollController: _scrollController,
itemHeight: _itemHeight,
itemCount: 12,
scrollOnTap: true,
child: ListWheelScrollView(
controller: _scrollController,
itemExtent: _itemHeight,
physics: FixedExtentScrollPhysics(),
diameterRatio: 3,
squeeze: 0.95,
children:
List.generate(12, (index) => MonthCardWidget(month: index + 1)),
),
);
}
}
class MonthCardWidget extends StatelessWidget {
final int month;
const MonthCardWidget({Key key, this.month}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
color: Colors.amber.shade300,
child: Center(
child: Text(DateFormat.MMMM().format(DateTime(2000, month))),
),
);
}
}
I'm having problems with mobx and radiobox: screen don't update when selected. I think it's a silly mistake, here are my main.dart, teste_store.dart and pubspec.yaml. The partial file .g was generated with build_runner and mobx_codegen.
A message appears when I run it: "No observables detected in the build method of Observer". I thought testeStore.selected was an observable and when changes triggers Observer to rebuild.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:teste_flutter/teste_store.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TesteStore testeStore = TesteStore();
#override
Widget build(BuildContext context) {
List<String> options = ["Option 1", "Option 2", "Option 3"];
return Scaffold(
appBar: AppBar(
title: Text("Test Flutter"),
),
body: Center(
child: Observer(
builder: (_){
return ListView.builder(
itemCount: options.length,
itemBuilder: (context, index) {
return RadioListTile<int>(
title: Text(options[index]),
value: index,
groupValue: testeStore.selected,
onChanged: testeStore.changeSelected,
);
},
);
}
)
),
);
}
}
teste_store.dart
import 'package:mobx/mobx.dart';
part 'teste_store.g.dart';
class TesteStore = _TesteStore with _$TesteStore;
abstract class _TesteStore with Store {
#observable
int selected = 0;
#action
void changeSelected(int newSelected) {
selected = newSelected;
}
}
pubspec.yaml
name: teste_flutter
description: A new Flutter application.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.0
mobx: ^1.2.1+2
flutter_mobx: ^1.1.0+2
provider: ^4.3.2+2
dev_dependencies:
flutter_test:
sdk: flutter
build_resolvers: ^1.3.10
mobx_codegen: ^1.1.0+1
build_runner: ^1.10.2
flutter:
uses-material-design: true
Edit 1 and 2:
I put the solution I found here and I shouldn't. Writing down in an answer box.
Talking to #BambinoUA, we found out a solution, just to add insight why his solution works.
I did put List view inside a Column with a sibling text widget with testeStore.selected like a text, now everything updates.
child: Observer(
builder: (_){
return Column(
children: [
Text(testeStore.selected.toString()),
ListView.builder(
itemCount: options.length,
itemBuilder: (context, index) {
return RadioListTile<int>(
title: Text(options[index]),
value: index,
groupValue: testeStore.selected,
onChanged: testeStore.changeSelected,
);
},
),
],
);
}
)
Back to original code I tried to only use print(testeStore.selected); before return inside builder of the Observer and it worked:
child: Observer(
builder: (_){
print(testeStore.selected);
return ListView.builder(
When I put inside the itemBuilder from RadioListTile don't work.
child: Observer(
builder: (_){
return ListView.builder(
itemCount: options.length,
itemBuilder: (context, index) {
print(testeStore.selected);
return RadioListTile<int>(
I assume Observer don't look changes inside other widgets builder.
I see that your TesteStore class is derived from private _TesteStore class. Maybe this is the case? And the error message looks reasonable. Try to make class with #observable public and re-build part file.
Update
Try to do intermediate assignments in Observer builder
final selected = testeStore.selected;
and then use new local variable selected inside inner builder.
Try to use ListView instead ListView.builder.
Try wrap with Observer not the List but RadioListTile.