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
since dart is call by reference, you need to do this :
});
rather than :
Probably the object returned has the same reference, you can change
setState
like this: