更新
更旧
typedef CreateIssueCallback = void Function({required String title, String? description, String? labels});
const IssueCreationView({super.key, required this.projectId, required this.model, required this.onCreateIssueTapped, required this.issueTitleController, required this.issueDescriptionController});
final TextEditingController issueTitleController;
final TextEditingController issueDescriptionController;
@override
State<IssueCreationView> createState() => _IssueCreationViewState();
}
class _IssueCreationViewState extends State<IssueCreationView> {
final double _bottomActionBoxHeight = 42;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return Stack(children: [
Container(
padding: EdgeInsets.only(bottom: _bottomActionBoxHeight),
child: SingleChildScrollView(
physics: const ScrollPhysics(),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
InputFieldWithTitle(title: 'Title (required)', placeHolder: 'Write a issue title', controller: widget.issueTitleController),
MarkdownInputBox(projectId: widget.projectId, controller: widget.issueDescriptionController),
// _buildTitle("Assignees"),
// _buildAssigneeSelector(),
]))),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
width: double.infinity,
height: _bottomActionBoxHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 10,
child: InkWell(
onTap: () {
Navigator.pop(context);
},
child: Container(
decoration: const BoxDecoration(color: Colors.white, borderRadius: BorderRadius.all(Radius.circular(4))),
alignment: Alignment.center,
child: Text('Cancel', style: TextStyle(color: colorScheme.secondary, fontSize: 14, fontWeight: FontWeight.w600)),
),
)),
child: widget.model.createIssueState == LoadState.loadingState
? const Center(child: CircularProgressIndicator())
: InkWell(
onTap: () {
var title = widget.issueTitleController.text;
var description = widget.issueDescriptionController.text;
var labels = widget.model.selectedLabels.map((e) => e.name).toList().join(",");
widget.onCreateIssueTapped(title: title, description: description, labels: labels);
},
child: Container(
alignment: Alignment.center,
decoration: const BoxDecoration(color: Color(0xFFFC6D26), borderRadius: BorderRadius.all(Radius.circular(4))),
child: const Text('Create issue', style: TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600)),
),
)),
Padding _buildTitle(String title) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Text(title, style: const TextStyle(color: Color(0XFF03162F), fontSize: 16, fontWeight: FontWeight.w600)),
);
}
var iconButton = IconButton(
key: const Key("goto-label-selector"),
onPressed: () async {
var result = await Navigator.of(context).pushNamed(LabelSelector.routeName, arguments: {"projectId": widget.projectId, "selectedIds": widget.model.selectedLabels.map((e) => e.id).toSet()});
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
if (result != null) {
List<Label> selectedLabels = result as List<Label>;
setState(() {
widget.model.onLabelSelect(selectedLabels);
});
}
},
icon: const Icon(Icons.add));
final List<Widget> children = [];
if (widget.model.selectedLabels.isEmpty) {
children.add(const Text("Select label(s)", style: TextStyle(color: Color(0XFF66696D), fontSize: 14, fontWeight: FontWeight.w400)));
} else {
for (var e in widget.model.selectedLabels) {
children.add(_buildLabelView(e));
}
}
children.add(iconButton);
return Container(
width: double.infinity,
decoration: const BoxDecoration(border: Border.fromBorderSide(BorderSide(color: Color(0XFFEAEAEA), width: 1)), borderRadius: BorderRadius.all(Radius.circular(4)), color: Colors.white),
padding: EdgeInsets.symmetric(vertical: widget.model.selectedLabels.isEmpty ? 0 : 8, horizontal: 8),
margin: const EdgeInsets.only(left: 16, right: 16),
child: Wrap(
spacing: 8,
runSpacing: 8,
alignment: widget.model.selectedLabels.isEmpty ? WrapAlignment.spaceBetween : WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
direction: Axis.horizontal,
children: children,
),
);
}
Widget _buildLabelView(Label label) {
return Container(
padding: const EdgeInsets.only(left: 4),
decoration: const BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(4)), color: Color(0XFFFFFAF7)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(width: 16, height: 16, decoration: BoxDecoration(color: Color(label.formatColor()))),
const VerticalDivider(width: 16),
Text(label.name),
IconButton(
onPressed: () {
setState(() {
widget.model.deleteSelectedLabel(label);
});
},
icon: Icon(Icons.close, color: Theme.of(context).primaryColor))
],
),
);
}
return Selectable<Member>(
onSelected: (members) => widget.model.onAssigneesSelect(members),
selectedItemBuilder: (member) => _buildAssigneeView(member),
selectorItemBuilder: (BuildContext context, int index, Member member) => AvatarAndName(username: member.username, avatarUrl: member.avatarUrl),
dataProviderProvider: () => MemberProvider(projectId: widget.projectId),
keyMapper: (member) => member.id,
placeHolder: "Assignees",
multiSelection: () => widget.model.isMultiAssignees(),
filter: (keyword, e) => e.username.toUpperCase().contains(keyword.toUpperCase()));
Widget _buildAssigneeView(Member member) {
return AvatarAndName(username: member.username, avatarUrl: member.avatarUrl);
var iconButton = IconButton(
key: const Key("goto-description-selector"),
onPressed: () async {
var result = await Navigator.of(context).pushNamed(DescriptionSelector.routeName, arguments: {
"projectId": widget.projectId,
"free": await widget.model.isFree(),
}) as Map<String, dynamic>;
var content = await result['content'];
widget.model.templateName = result['templateName'];
widget.issueDescriptionController.text = content;
setState(() {});
},
icon: const Icon(Icons.add));
final List<Widget> children = [];
children.add(Text(widget.model.templateName ?? 'Choose a template', style: const TextStyle(color: Color(0XFF66696D), fontSize: 14, fontWeight: FontWeight.w400)));
children.add(iconButton);
return Container(
width: double.infinity,
decoration: const BoxDecoration(border: Border.fromBorderSide(BorderSide(color: Color(0XFFEAEAEA), width: 1)), borderRadius: BorderRadius.all(Radius.circular(4)), color: Colors.white),
padding: EdgeInsets.symmetric(vertical: widget.model.selectedAssignees.isEmpty ? 0 : 8, horizontal: 8),
margin: const EdgeInsets.only(left: 16, right: 16),
child: Wrap(
spacing: 8,
runSpacing: 8,
alignment: widget.model.selectedAssignees.isEmpty ? WrapAlignment.spaceBetween : WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
direction: Axis.horizontal,
children: children,
),
);
}