stacked icon indicating copy to clipboard operation
stacked copied to clipboard

Accessing params in a widget's viewModel

Open pieterbergmans opened this issue 2 years ago • 4 comments

Describe the bug

I built a widget, called OrbitWidget that also has a viewModel called OrbitViewModel. Since I didn’t want a route generated for this widget, I didn’t use stacked create view …, I just did a File New and extended the OrbitWidget with StackedView<OrbitalViewModel>.

I’m embedding the OrbitWidget into my SampleView. When a user navigates to the SampleView, the SampleViewModel gets graph data from our API which, in turn, gets fed as a parameter into the OrbitWidget(graphs: graphs). But, I’m having a hard time accessing the graph data from my OrbitViewModel. Can anyone tell me how to go about this? Here is what I have…

SampleView

class SampleView extends StackedView<SampleViewModel> {
  const SampleView({
    Key? key,
  }) : super(key: key);

  @override
  Widget builder(
    BuildContext context,
    SampleViewModel viewModel,
    Widget? child,
  ) {
    return OrbitWidget(graphs: viewModel.graphs);
  }
}

SampleViewModel

class SampleViewModel extends BaseViewModel {
  List<Graph> graphs = await getGraphDataFromApi();
}

OrbitWidget

class OrbitWidget extends StackedView<OrbitViewModel> {
  final List<Graph> graphs;
  const OrbitWidget({
    Key? key,
    required this.graphs,
  }) : super(key: key);

  @override
  Widget builder(
    BuildContext context,
    OrbitViewModel viewModel,
    Widget? child,
  ) {
    print('OrbitWidget, graphs: ${graphs.length}'); // prints: OrbitWidget, graphs: 1
    return DisplayGraphs(viewModel.graphs);
  } 

 @override
  OrbitViewModel viewModelBuilder(
    BuildContext context,
  ) =>
      // add graphs as a param to viewModel
      OrbitViewModel(graphs: graphs);
}

OrbitViewModel…the viewModel has several functions which manipulates the data which is why I need access to it

class OrbitViewModel extends BaseViewModel {
  // param
  List<Graph> graphs;
  OrbitViewModel({required this.graphs}) {
    logger.i('OrbitViewModel, graphs: ${graphs.length}'); // prints: OrbitViewModel, graphs: 0
  }
}

To reproduce

Expected behavior

No response

Screenshots

No response

Additional Context

No response

pieterbergmans avatar Jul 27 '23 23:07 pieterbergmans

I fixed this by adding a key to OrbitalWidget, like so...

class SampleView extends StackedView<SampleViewModel> {
  const SampleView({
    Key? key,
  }) : super(key: key);

  @override
  Widget builder(
    BuildContext context,
    SampleViewModel viewModel,
    Widget? child,
  ) {
    return OrbitWidget(
      graphs: viewModel.graphs,
      key: GlobalKey(),
   );
  }
}

pieterbergmans avatar Jul 28 '23 15:07 pieterbergmans

Hi @pieterbergmans , how are you?

class SampleViewModel extends BaseViewModel {
  List<Graph> graphs = await getGraphDataFromApi();
}

What is that ☝? You can't use a code like that. The await expression can only be used in an async function.

I would suggest to do something like below:

List<Graph> _graphs;
List<Graph> get graphs => _graphs;

Future<void> initialize() async {
  _graphs = await getGraphDataFromApi();
}

Then, on the widget you access the graphs with viewModel.graphs but once is fetched, the Future has to be executed first for the graphs to be available, if not you only have a Future of something not the actual value.

ferrarafer avatar Aug 07 '23 20:08 ferrarafer

@pieterbergmans Can this be closed?

sebastianbuechler avatar Sep 17 '24 07:09 sebastianbuechler

@sebastianbuechler - yes, it can.

pieterbergmans avatar Sep 17 '24 16:09 pieterbergmans