I'm pretty new both to Flutter/Dart and Stack Overflow. I started with some trip ETA code and modified it to be an initiative turn tracker for a fantasy RPG tabletop game.
My intent is to keep the End Encounter button on the screen at all times, and have the timeline content scroll in a window above it.
I added a SingleChildScrollView into _TurnSectionState at line 178, but as soon as I add a Container into it I get the overflow. I tried for about 3 hours before asking for help here.
final data = _data(1);
return Scaffold(
appBar: TitleAppBar(widget._title),
body: Center(
child: SingleChildScrollView(
child: SizedBox(
width: 380.0,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(20.0),
child: _MissionName(
timeSliceInfo: data,
),
),
const Divider(height: 1.0),
_TurnSection(processes: data.deliveryProcesses),
const Divider(height: 1.0),
const Padding(
padding: EdgeInsets.all(20.0),
child: _OnTimeBar(),
),
],
),
),
),
),
);
}
}
// Mission name
class _MissionName extends StatelessWidget {
const _MissionName({
Key? key,
required this.timeSliceInfo,
}) : super(key: key);
final _TimeSliceInfo timeSliceInfo;
@override
Widget build(BuildContext context) {
return Row(
children: [
Text(
missions[currentMission].getTitle(),
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
],
);
}
}
// Lists items in each turn
class _InnerTimeline extends StatefulWidget {
const _InnerTimeline({
required this.messages,
});
final List<ActivationLineItem> messages;
@override
State<_InnerTimeline> createState() => _InnerTimelinePageState();
}
class _InnerTimelinePageState extends State<_InnerTimeline> {
@override
initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
bool isEdgeIndex(int index) {
return index == 0 || index == widget.messages.length 1;
}
// Interior connector lines
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: FixedTimeline.tileBuilder(
theme: TimelineTheme.of(context).copyWith(
nodePosition: 0,
connectorTheme: TimelineTheme.of(context).connectorTheme.copyWith(
thickness: 1.0,
),
indicatorTheme: TimelineTheme.of(context).indicatorTheme.copyWith(
size: 10.0,
position: 0.5,
),
),
builder: TimelineTileBuilder(
indicatorBuilder: (_, index) =>
!isEdgeIndex(index) ? Indicator.outlined(borderWidth: 1.0) : null,
startConnectorBuilder: (_, index) => Connector.solidLine(),
endConnectorBuilder: (_, index) => Connector.solidLine(),
contentsBuilder: (_, index) {
if (isEdgeIndex(index)) {
return null;
}
// Line item (init value icon text)
return Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Row(children: [
Text(widget.messages[index - 1].tick.toString()),
const SizedBox(width: 6),
Image(
image: AssetImage(widget.messages[index - 1].thumbnail),
width: 20,
height: 20),
const SizedBox(width: 6),
Expanded(
child: Text(widget.messages[index - 1].message,
overflow: TextOverflow.ellipsis, maxLines: 1))
]));
},
itemExtentBuilder: (_, index) => isEdgeIndex(index) ? 10.0 : 30.0,
nodeItemOverlapBuilder: (_, index) =>
isEdgeIndex(index) ? true : null,
itemCount: widget.messages.length 2,
),
),
);
}
}
// Outer timeline (List of turns)
class _TurnSection extends StatefulWidget {
const _TurnSection({Key? key, required processes})
: _processes = processes,
super(key: key);
final List<TurnContents> _processes;
@override
_TurnSectionState createState() => _TurnSectionState();
}
class _TurnSectionState extends State<_TurnSection> {
bool isExpanded = false;
@override
Widget build(BuildContext context) {
return DefaultTextStyle(
style: const TextStyle(
color: Color(0xff9b9b9b), // Nobel Gray
fontSize: 12.5,
),
child: SingleChildScrollView(
child: SizedBox(
height: 480,
child: Column(
children: [
FixedTimeline.tileBuilder(
theme: TimelineThemeData(
nodePosition: 0,
color: const Color(0xff989898), // Spanish Gray
indicatorTheme: const IndicatorThemeData(
position: 0,
size: 20.0, // Outer timeline circle size
),
connectorTheme: const ConnectorThemeData(
thickness: 2.5, // Outer timeline line thickness
),
),
builder: TimelineTileBuilder.connected(
connectionDirection: ConnectionDirection.before,
itemCount: widget._processes.length,
contentsBuilder: (_, index) {
if (widget._processes[index].isCompleted) return null;
return GestureDetector(
onTap: () {
int turnNum = widget._processes[index].getTurnNum();
if (turnNum == currentTurn) {
// Ask if ready for next turn
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Next Turn?'),
content: Text('Ready to start turn ?'
(currentTurn 1).toString()),
actions: <Widget>[
TextButton(
onPressed: () =>
Navigator.pop(context, 'Yes'),
child: const Text('Yes'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'No'),
child: const Text('No'),
),
],
),
).then((value) {
if (value == 'Yes') {
currentTurn ; // Move to the next turn
setState(() {}); // Draw the screen
}
});
}
},
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
widget._processes[index]
.getName(), // Turn name font size
style:
DefaultTextStyle.of(context).style.copyWith(
fontSize: 18.0,
),
),
_InnerTimeline(
messages:
widget._processes[index].getMessages()),
],
),
),
);
},
indicatorBuilder: (_, index) {
if (index <= currentTurn) {
return const DotIndicator(
color: Color(0xff66c97f), // Emerald Green dot
child: Icon(
Icons.check,
color: Colors.white,
size: 12.0,
),
);
} else {
return const OutlinedDotIndicator(
borderWidth: 2.5,
);
}
},
connectorBuilder: (_, index, ___) => SolidLineConnector(
color: index <= currentTurn
? const Color(0xff66c97f) // Emerald Green connector
: null, // Green
),
),
),
],
),
),
),
);
}
}
class _OnTimeBar extends StatelessWidget {
const _OnTimeBar({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
MaterialButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('On Time!'),
),
);
},
elevation: 0,
shape: const StadiumBorder(),
color: const Color(0xff66c97f),
textColor: Colors.white,
child: const Text('End Encounter'),
),
const Spacer(),
const SizedBox(width: 12.0),
],
);
}
}
/*
List<TurnContents> buildTimeline() {
List<TurnContents> turns;
return turns;
}
*/
_TimeSliceInfo _data(int id) => _TimeSliceInfo(
id: id,
date: DateTime.now(),
deliveryProcesses: ImpulseBuilder().buildTimeline(),
);
class _TimeSliceInfo {
const _TimeSliceInfo({
required this.id,
required this.date,
required this.deliveryProcesses,
});
final int id;
final DateTime date;
final List<TurnContents> deliveryProcesses;```
}
[1]: https://i.stack.imgur.com/mGd5X.png
CodePudding user response:
use Expanded and in SingleChildScrollView add physics:ScrollPhysics()
Expanded(
child: Container(
child: SingleChildScrollView(
physics: ScrollPhysics(),
child: Column(
children: []
)
)
)
);