skip to Main Content

On one of my flutter pages, there is a list of Stages (via _buildStagesList). When a stage card is clicked, an API call returns the configuration for that stage and sets the state _selectedStageConfig, which is then used in the right expanded portion.

State variable of interest:

   List<dynamic>? _selectedStageConfig;

Code where the state is set:

  setState(() {
    _selectedStageConfig = stageConfig;
  });

This works the first time I click on a stage. However, the next time I click on it, the changes do not reflect (essentially looks like the extended portion is not refreshing/rebuilding).

                  Expanded(
                    flex: 2,
                    child: 
                      _selectedStageConfig == null ? 
                        const Center(child: Text('Select a stage to view details'))
                      : FormBuilder(
                          formConfigJson: jsonEncode(_selectedStageConfig),   <------
                          onFormSubmitted: (formData) {
                            print('Form submitted with data: $formData');
                            // Handle form submission
                          },
                        )
                  )

I have gone through several SO links, and could not comprehend why despite having the _selectedStageConfig reference directly inside the build method, it’s not re-building when changes are done to the _selectedStageConfig state.

The entire code is below:

// In project_detail_view.dart
import 'dart:convert';

import 'package:buildwiz/widgets/forms/form.dart';
import 'package:flutter/material.dart';
import 'package:buildwiz/models/project_model.dart';
import 'package:buildwiz/models/stage_model.dart';
import 'package:buildwiz/services/api_service.dart';

class ProjectDetailView extends StatefulWidget {
  final int pid;
  final int wid;

  const ProjectDetailView({Key? key, required this.pid, required this.wid}) : super(key: key);

  @override
  _ProjectDetailViewState createState() => _ProjectDetailViewState();
}

class _ProjectDetailViewState extends State<ProjectDetailView> {
  final ApiService _apiService = ApiService();
  Project? _project;
  List<Stage> _stages = [];
  List<dynamic>? _selectedStageConfig;
  bool _isLoading = true;
  String? _error;

  @override
  void initState() {
    super.initState();
    _fetchProjectStages();
  }

  Future<void> _fetchProjectStages() async {
    try {
      setState(() {
        _isLoading = true;
        _error = null;
      });
      _project = await _apiService.getProject(widget.wid, widget.pid);
      final stages = await _apiService.getProjectStages(widget.wid, widget.pid);
      setState(() {
        _stages = stages;
        _isLoading = false;
      });
    } catch (e) {
      setState(() {
        _error = 'Failed to load stages: $e';
        _isLoading = false;
      });
    }
  }

  Future<void> _fetchStageDetails(int stageId) async {
    try {
      final stageConfig = await _apiService.getStageConfig(
        widget.wid,
        widget.pid,
        stageId,
      );
      setState(() {
        _selectedStageConfig = stageConfig;
      });
    } catch (e) {
      print('Failed to load stage details: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        border: Border(
          left: BorderSide(color: Colors.grey[300]!, width: 1),
        ),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Text(
              _project!.title,
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ),
          Expanded(
            child: _isLoading
                ? const Center(child: CircularProgressIndicator())
                : _error != null
                    ? Center(child: Text(_error!))
                    : Row(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Expanded(
                            flex: 1,
                            child: _buildStagesList(),
                          ),
                          Expanded(
                            flex: 2,
                            child: 
                              _selectedStageConfig == null ? 
                                const Center(child: Text('Select a stage to view details'))
                              : FormBuilder(
                                  formConfigJson: jsonEncode(_selectedStageConfig),
                                  onFormSubmitted: (formData) {
                                    print('Form submitted with data: $formData');
                                    // Handle form submission
                                  },
                                )
                          ),
                        ],
                      ),
          ),
        ],
      ),
    );
  }

  Widget _buildStagesList() {
    return ListView.builder(
      itemCount: _stages.length,
      itemBuilder: (context, index) {
        final stage = _stages[index];
        return Card(
          margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: ListTile(
            title: Text(stage.title),
            subtitle: _buildStatusPill(stage.status),
            onTap: () => _fetchStageDetails(stage.id),
          ),
        );
      },
    );
  }

  Widget _buildStatusPill(String status) {
    Color pillColor;
    switch (status.toLowerCase()) {
      case 'active':
        pillColor = Colors.green;
        break;
      case 'inactive':
        pillColor = Colors.red;
        break;
      default:
        pillColor = Colors.grey;
    }

    return Container(
      padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: pillColor.withOpacity(0.2),
        borderRadius: BorderRadius.circular(12),
      ),
      child: Text(
        status,
        style: TextStyle(color: pillColor, fontWeight: FontWeight.bold),
      ),
    );
  }
}

Can someone point out where I’m going wrong?

2

Answers


  1. since dart is call by reference, you need to do this :

    setState(() {
    _selectedStageConfig.clear();
    _selectedStageConfig.addAll(stageConfig);
    

    });

    rather than :

    setState(() {
        _selectedStageConfig = stageConfig;
      });
    
    Login or Signup to reply.
  2. Probably the object returned has the same reference, you can change setState like this:

    setState(() {
      _selectedStageConfig = List.from(stageConfig);
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search