So thanks to @pskink I have a working version... using a record makes it quite need although I don't think that is the thing that actually fixed it - rather using a separate future returned from _loadData and then adding a load of ? and ! to override nulls - but I just don't have time to do all the differential diagnosis to see exactly what fixed what (feel free to add comments to make my code more robust).
As an aside I like the fact that retrieving elements from records makes my code look a bit more like Perl :)
import 'package:flutter/material.dart';
import 'load_xml.dart';
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final String xmlAssetPath = 'data.xml';
Future<(DataTree, Group)>? initFuture;
DataTree? dataTree;
Category? selectedCategory; // = Category(name: 'not_set', groupNames: []);
Group? selectedGroup;
// Group selectedGroup = Group(name: "not_set", items: []);
String? groupName; // ="Citrus";
Item? selectedItem; // = Item(name: "Orange");
int selectedItemIndex = 0;
Future<(DataTree, Group)> _loadData() async {
dataTree = await loadXml(xmlAssetPath);
debugPrint ("Loading data tree...");
for (var category in dataTree!.categories) {
debugPrint ("Loading category ${category.name}...");
if ((category.isDefault ?? false) && (category.defaultGroupName != null)) {
debugPrint ("Setting groupName to ${category.defaultGroupName}...");
groupName = category.defaultGroupName!;
}
}
debugPrint ("Loading selected group $groupName...");
selectedGroup = await loadGroup(xmlAssetPath, groupName!);
for (var item in selectedGroup!.items) {
debugPrint ("Loading item ${item.name}...");
if (item.name == selectedGroup!.defaultItemName) {
selectedItem = item;
selectedItemIndex = selectedGroup!.items.indexOf(selectedItem!);
}
}
return (dataTree!, selectedGroup!);
}
@override
void initState() {
super.initState();
initFuture = _loadData();
}
Widget build(BuildContext context) {
return FutureBuilder<(DataTree, Group)> (
future: initFuture,
builder: (BuildContext context, AsyncSnapshot<(DataTree, Group)> snapshot) {
if (snapshot.hasData) {
return MaterialApp (
title: snapshot.data!.$2.name,
home: ListView.builder(
itemBuilder: (context, index) => Card(
key: ValueKey(snapshot.data!.$2.items[index].name),
child: ListTile(
title: Text(snapshot.data!.$2.items[index].name),
subtitle: Text((snapshot.data!.$2.items[index].name==selectedItem!.name) ? "Selected" : "Not selected"),
),
),
itemCount: snapshot.data!.$2.items.length,
)
);
} else {
return Container();
}
}
);
}
}