Skip to content

Commit 56a247e

Browse files
committed
Add github_summary.dart
1 parent ce68852 commit 56a247e

File tree

1 file changed

+234
-0
lines changed

1 file changed

+234
-0
lines changed

lib/src/github_summary.dart

+234
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:fluttericon/octicons_icons.dart';
3+
import 'package:github/github.dart';
4+
import 'package:url_launcher/url_launcher_string.dart';
5+
6+
class GitHubSummary extends StatefulWidget {
7+
const GitHubSummary({required this.gitHub, super.key});
8+
final GitHub gitHub;
9+
10+
@override
11+
State<GitHubSummary> createState() => _GitHubSummaryState();
12+
}
13+
14+
class _GitHubSummaryState extends State<GitHubSummary> {
15+
int _selectedIndex = 0;
16+
17+
@override
18+
Widget build(BuildContext context) {
19+
return Row(
20+
children: [
21+
NavigationRail(
22+
selectedIndex: _selectedIndex,
23+
onDestinationSelected: (index) {
24+
setState(() {
25+
_selectedIndex = index;
26+
});
27+
},
28+
labelType: NavigationRailLabelType.selected,
29+
destinations: const [
30+
NavigationRailDestination(
31+
icon: Icon(Octicons.repo),
32+
label: Text('Repositories'),
33+
),
34+
NavigationRailDestination(
35+
icon: Icon(Octicons.issue_opened),
36+
label: Text('Assigned Issues'),
37+
),
38+
NavigationRailDestination(
39+
icon: Icon(Octicons.git_pull_request),
40+
label: Text('Pull Requests'),
41+
),
42+
],
43+
),
44+
const VerticalDivider(thickness: 1, width: 1),
45+
// This is the main content.
46+
Expanded(
47+
child: IndexedStack(
48+
index: _selectedIndex,
49+
children: [
50+
RepositoriesList(gitHub: widget.gitHub),
51+
AssignedIssuesList(gitHub: widget.gitHub),
52+
PullRequestsList(gitHub: widget.gitHub),
53+
],
54+
),
55+
),
56+
],
57+
);
58+
}
59+
}
60+
61+
class RepositoriesList extends StatefulWidget {
62+
const RepositoriesList({required this.gitHub, super.key});
63+
final GitHub gitHub;
64+
65+
@override
66+
State<RepositoriesList> createState() => _RepositoriesListState();
67+
}
68+
69+
class _RepositoriesListState extends State<RepositoriesList> {
70+
@override
71+
initState() {
72+
super.initState();
73+
_repositories = widget.gitHub.repositories.listRepositories().toList();
74+
}
75+
76+
late Future<List<Repository>> _repositories;
77+
78+
@override
79+
Widget build(BuildContext context) {
80+
return FutureBuilder<List<Repository>>(
81+
future: _repositories,
82+
builder: (context, snapshot) {
83+
if (snapshot.hasError) {
84+
return Center(child: Text('${snapshot.error}'));
85+
}
86+
if (!snapshot.hasData) {
87+
return const Center(child: CircularProgressIndicator());
88+
}
89+
var repositories = snapshot.data;
90+
return ListView.builder(
91+
primary: false,
92+
itemBuilder: (context, index) {
93+
var repository = repositories[index];
94+
return ListTile(
95+
title:
96+
Text('${repository.owner?.login ?? ''}/${repository.name}'),
97+
subtitle: Text(repository.description),
98+
onTap: () => _launchUrl(this, repository.htmlUrl),
99+
);
100+
},
101+
itemCount: repositories!.length,
102+
);
103+
},
104+
);
105+
}
106+
}
107+
108+
class AssignedIssuesList extends StatefulWidget {
109+
const AssignedIssuesList({required this.gitHub, super.key});
110+
final GitHub gitHub;
111+
112+
@override
113+
State<AssignedIssuesList> createState() => _AssignedIssuesListState();
114+
}
115+
116+
class _AssignedIssuesListState extends State<AssignedIssuesList> {
117+
@override
118+
initState() {
119+
super.initState();
120+
_assignedIssues = widget.gitHub.issues.listByUser().toList();
121+
}
122+
123+
late Future<List<Issue>> _assignedIssues;
124+
125+
@override
126+
Widget build(BuildContext context) {
127+
return FutureBuilder<List<Issue>>(
128+
future: _assignedIssues,
129+
builder: (context, snapshot) {
130+
if (snapshot.hasError) {
131+
return Center(child: Text('${snapshot.error}'));
132+
}
133+
if (!snapshot.hasData) {
134+
return const Center(child: CircularProgressIndicator());
135+
}
136+
var assignedIssues = snapshot.data;
137+
return ListView.builder(
138+
primary: false,
139+
itemBuilder: (context, index) {
140+
var assignedIssue = assignedIssues[index];
141+
return ListTile(
142+
title: Text(assignedIssue.title),
143+
subtitle: Text('${_nameWithOwner(assignedIssue)} '
144+
'Issue #${assignedIssue.number} '
145+
'opened by ${assignedIssue.user?.login ?? ''}'),
146+
onTap: () => _launchUrl(this, assignedIssue.htmlUrl),
147+
);
148+
},
149+
itemCount: assignedIssues!.length,
150+
);
151+
},
152+
);
153+
}
154+
155+
String _nameWithOwner(Issue assignedIssue) {
156+
final endIndex = assignedIssue.url.lastIndexOf('/issues/');
157+
return assignedIssue.url.substring(29, endIndex);
158+
}
159+
}
160+
161+
class PullRequestsList extends StatefulWidget {
162+
const PullRequestsList({required this.gitHub, super.key});
163+
final GitHub gitHub;
164+
165+
@override
166+
State<PullRequestsList> createState() => _PullRequestsListState();
167+
}
168+
169+
class _PullRequestsListState extends State<PullRequestsList> {
170+
@override
171+
initState() {
172+
super.initState();
173+
_pullRequests = widget.gitHub.pullRequests
174+
.list(RepositorySlug('flutter', 'flutter'))
175+
.toList();
176+
}
177+
178+
late Future<List<PullRequest>> _pullRequests;
179+
180+
@override
181+
Widget build(BuildContext context) {
182+
return FutureBuilder<List<PullRequest>>(
183+
future: _pullRequests,
184+
builder: (context, snapshot) {
185+
if (snapshot.hasError) {
186+
return Center(child: Text('${snapshot.error}'));
187+
}
188+
if (!snapshot.hasData) {
189+
return const Center(child: CircularProgressIndicator());
190+
}
191+
var pullRequests = snapshot.data;
192+
return ListView.builder(
193+
primary: false,
194+
itemBuilder: (context, index) {
195+
var pullRequest = pullRequests[index];
196+
return ListTile(
197+
title: Text(pullRequest.title ?? ''),
198+
subtitle: Text('flutter/flutter '
199+
'PR #${pullRequest.number} '
200+
'opened by ${pullRequest.user?.login ?? ''} '
201+
'(${pullRequest.state?.toLowerCase() ?? ''})'),
202+
onTap: () => _launchUrl(this, pullRequest.htmlUrl ?? ''),
203+
);
204+
},
205+
itemCount: pullRequests!.length,
206+
);
207+
},
208+
);
209+
}
210+
}
211+
212+
Future<void> _launchUrl(State state, String url) async {
213+
if (await canLaunchUrlString(url)) {
214+
await launchUrlString(url);
215+
} else {
216+
if (state.mounted) {
217+
return showDialog(
218+
context: state.context,
219+
builder: (context) => AlertDialog(
220+
title: const Text('Navigation error'),
221+
content: Text('Could not launch $url'),
222+
actions: <Widget>[
223+
TextButton(
224+
onPressed: () {
225+
Navigator.of(context).pop();
226+
},
227+
child: const Text('Close'),
228+
),
229+
],
230+
),
231+
);
232+
}
233+
}
234+
}

0 commit comments

Comments
 (0)