Skip to content

Instantly share code, notes, and snippets.

@kingdomseed
Created November 7, 2024 14:18
Show Gist options
  • Select an option

  • Save kingdomseed/639b0e79451716775f4a3c02641bb505 to your computer and use it in GitHub Desktop.

Select an option

Save kingdomseed/639b0e79451716775f4a3c02641bb505 to your computer and use it in GitHub Desktop.
Editable Scene List Widget
// Flutter imports:
import 'package:flutter/material.dart';
// Project imports:
import '../../../core/domain_models/scene.dart';
class EditableSceneItem extends StatefulWidget {
final Scene scene;
final Function(String, String, int) onSave;
final VoidCallback onDelete;
final bool isNew;
final TextScaler textScaler;
const EditableSceneItem({
super.key,
required this.scene,
required this.onSave,
required this.onDelete,
required this.textScaler,
this.isNew = false,
});
@override
EditableSceneItemState createState() => EditableSceneItemState();
}
class EditableSceneItemState extends State<EditableSceneItem> {
late TextEditingController _titleController;
late TextEditingController _summaryController;
late int _chaosFactor;
bool _isEditing = false;
@override
void initState() {
super.initState();
_titleController = TextEditingController(text: widget.scene.title);
_summaryController = TextEditingController(text: widget.scene.sceneSummary);
_chaosFactor = widget.scene.sceneChaosFactor;
_isEditing = widget.isNew;
}
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.symmetric(vertical: 8),
child: AnimatedCrossFade(
duration: const Duration(milliseconds: 300),
crossFadeState:
_isEditing ? CrossFadeState.showSecond : CrossFadeState.showFirst,
firstChild: _buildViewMode(),
secondChild: _buildEditMode(),
),
);
}
Widget _buildViewMode() {
return ExpansionTile(
title: Text(
widget.scene.title,
textScaler: widget.textScaler,
),
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.scene.sceneSummary,
textScaler: widget.textScaler,
),
const SizedBox(height: 8),
Text(
'Chaos Factor: ${widget.scene.sceneChaosFactor}',
textScaler: widget.textScaler,
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Semantics(
button: true,
onTapHint: 'Edit scene details',
child: TextButton(
onPressed: () => setState(() => _isEditing = true),
child: Text(
'Edit',
textScaler: widget.textScaler,
),
),
),
Semantics(
button: true,
onTapHint: 'Delete this scene',
child: TextButton(
onPressed: widget.onDelete,
style: TextButton.styleFrom(foregroundColor: Colors.red),
child: Text(
'Delete',
textScaler: widget.textScaler,
),
),
),
],
),
],
),
),
],
);
}
Widget _buildEditMode() {
return Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _titleController,
decoration: InputDecoration(
labelText: 'Title',
filled: true,
labelStyle: TextStyle(
fontSize: Theme.of(context).textTheme.bodyLarge?.fontSize,
),
),
onTapOutside: (_) => FocusScope.of(context).unfocus(),
),
const SizedBox(height: 16),
TextField(
controller: _summaryController,
decoration: InputDecoration(
labelText: 'Summary',
filled: true,
labelStyle: TextStyle(
fontSize: Theme.of(context).textTheme.bodyLarge?.fontSize,
),
),
maxLines: 3,
),
const SizedBox(height: 16),
MergeSemantics(
child: Column(
children: [
Text(
'Chaos Factor: $_chaosFactor',
textScaler: widget.textScaler,
),
Semantics(
value: '$_chaosFactor',
increasedValue: '${_chaosFactor + 1}',
decreasedValue: '${_chaosFactor - 1}',
onIncrease: _chaosFactor < 9
? () => setState(() => _chaosFactor = _chaosFactor + 1)
: null,
onDecrease: _chaosFactor > 1
? () => setState(() => _chaosFactor = _chaosFactor - 1)
: null,
child: Slider(
value: _chaosFactor.toDouble(),
min: 1,
max: 9,
divisions: 8,
label: _chaosFactor.toString(),
onChanged: (value) {
setState(() => _chaosFactor = value.round());
},
),
),
],
),
),
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Semantics(
button: true,
label: "Cancel editing",
child: TextButton(
onPressed: () {
if (widget.isNew) {
widget.onDelete();
} else {
setState(() {
_isEditing = false;
_titleController.text = widget.scene.title;
_summaryController.text = widget.scene.sceneSummary;
_chaosFactor = widget.scene.sceneChaosFactor;
});
}
},
child: Text(
'Cancel',
textScaler: widget.textScaler,
),
),
),
Semantics(
button: true,
label: "Save scene",
child: ElevatedButton(
onPressed: () {
widget.onSave(
_titleController.text,
_summaryController.text,
_chaosFactor,
);
setState(() => _isEditing = false);
},
child: Text(
'Save',
textScaler: widget.textScaler,
),
),
),
],
),
),
],
),
),
);
}
@override
void dispose() {
_titleController.dispose();
_summaryController.dispose();
super.dispose();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment