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