Search code examples
flutterdartpluto-grid

How do I dynamically load a PlutoGrid from an API call that returns JSON?


I'm having an issue with dynamically loading data into a PlutoGrid.

I'm not getting any error messages.

I am making sure that the JSON data I am getting from my API is populating a list of strings that is mapped to a list of PlutoRow objects. When I statically populate a list of strings and map that to the list of PlutoRow objects, that will display in the PlutoGrid. When I step thru my code, I noticed the following:

  1. The Widget build() function executes first and the rowsProviders list object is empty
  2. The initiState() function executes next and populates the rowsProviders list object (verified)
  3. The Widget build() function executes again and the rowsProviders list object now has data when I set the rows property of the PlutoGrid to it. HOWEVER, this has NO EFFECT on the grid. It has it's column headers, but it has zero rows and there is no error message.

I have provided my code below. I have stripped out everything but this PlutoGrid. This is the code I am trying to debug.

import 'dart:convert';
import 'package:http/http.dart' as http;
import "package:flutter/material.dart";
import 'package:pluto_grid/pluto_grid.dart';

const String API_URL_LLMT_PROVIDER_ROUTE = "http://127.0.0.1:5000/get_mt_providers";


void main() 
{
    runApp(const MyApp());
}

class MyApp extends StatelessWidget 
{
    const MyApp({Key? key}) : super(key: key);

    @override
    Widget build(BuildContext context) 
    {
        return MaterialApp
        (
            title: 'LL Translate',
            theme: ThemeData(primarySwatch: Colors.blue,),
            home: const DetectorPage(title: 'LLTS MT Tools'),
            debugShowCheckedModeBanner: true,
        );
    }
}

class DetectorPage extends StatefulWidget 
{
    const DetectorPage({Key? key, required this.title}) : super(key: key);
    final String title;

    @override
    State<DetectorPage> createState() => _DetectorPageState();
}

class _DetectorPageState extends State<DetectorPage>
{
    final List<PlutoColumn> columnsProviders = [];
    List<PlutoRow> rowsProviders = [];
    List<String> providers = [];
    
    @override
    void initState()
    {
        super.initState();

        columnsProviders.addAll 
        (
            [
                PlutoColumn
                (
                    width: 400,
                    backgroundColor: Colors.blue,
                    title: 'MT Provider',
                    field: 'MT Provider Tested',                        
                    type: PlutoColumnType.text(),
                    enableRowChecked: true,
                ),
                PlutoColumn
                (
                    width: 95,
                    backgroundColor: Colors.blue,
                    title: 'WER', 
                    field: 'Leven',
                    type: PlutoColumnType.text(),
                    enableFilterMenuItem: true,
                    textAlign: PlutoColumnTextAlign.center,
                    titleTextAlign: PlutoColumnTextAlign.center,
                ),
                PlutoColumn
                (
                    width: 100,
                    backgroundColor: Colors.blue,
                    title: 'BLEU',
                    field: 'BLEU',
                    type: PlutoColumnType.text(),
                    enableFilterMenuItem: false,
                    textAlign: PlutoColumnTextAlign.center,
                    titleTextAlign: PlutoColumnTextAlign.center,
                ),
                PlutoColumn
                (
                    width: 120,
                    backgroundColor: Colors.blue,
                    title: 'Combined',
                    field: 'Combined',
                    type: PlutoColumnType.text(),
                    enableFilterMenuItem: false,
                    textAlign: PlutoColumnTextAlign.center,
                    titleTextAlign: PlutoColumnTextAlign.center,
                ),
            ]
        );

        loadMtProviders();
    }

    void loadMtProviders() async 
    {
        // List<String> providersList = providers.cast<String>().toList();
        // List<String> providers = ['Google', 'Microsoft'];
        // List<String> providers = await getMtProviders();
        // providers = ['Google', 'Microsoft'];
        providers = await getMtProviders();

        rowsProviders = providers.map((item) 
        {
            return PlutoRow
            (
                cells: 
                {
                    'MT Provider Tested': 
                        PlutoCell
                        (
                            value: item,
                            
                        ), 
                    'Leven': PlutoCell(value: ''),
                    'BLEU': PlutoCell(value: ''),
                    'Combined': PlutoCell(value: ''),
                },
            );
        }).toList();


        setState(() 
        {

        });
    }
    
    Future<List<String>> getMtProviders() async 
    {
        var url = Uri.parse(API_URL_LLMT_PROVIDER_ROUTE);
        var response = await http.get(url);

        if (response.statusCode == 200)
        {
            var value = response.body;
            var jsonResponse = jsonDecode(value);

            List<String> result = jsonResponse.cast<String>();
            
            return result;
        } 
        else 
        {
            print('Request failed with status: ${response.statusCode}.');
            return ['0'];
        }
    }

    @override
    Widget build(BuildContext context) 
    {
        return Scaffold
        (
            body: SizedBox
            (
                child: PlutoGrid
                (
                    columns: columnsProviders,
                    rows: rowsProviders,
                ),
            ) 

        );
    }
}

Solution

  • As @pixel pointed out, this is about state management in flutter. An easy way to do this is by using a StateManager object. PlutoGrid actually has a PlutoGridStateManager class for this purpose.

    You first define it in your PageState class:

    class _DetectorPageState extends State<DetectorPage>
    {
        late PlutoGridStateManager stateManagerProviders;
        ...
    }
    

    And then when you populate the grid, you add it to the PlutoGridStateManager:

    setState(() 
    {
        stateManagerProviders.appendRows(rowsProviders);
    });
    

    And finally, you wire up PlutoGrid events to the PlutoGridStateManager like so:

    @override
    Widget build(BuildContext context) 
    {
        ...
    
        child: PlutoGrid
        (
            columns: columnsProviders,
            rows: rowsProviders,
            onLoaded: (PlutoGridOnLoadedEvent event) 
            {
                stateManagerProviders = event.stateManager;
                stateManagerSummary.setSelectingMode(PlutoGridSelectingMode.row);
            },
        ),
    }
    

    This is a simple example. You can maintain the state of your PlutoGrid by wiring up additional events here.