1- // Settings page.
2-
31import 'package:flutter/material.dart' ;
42import 'package:flutter_riverpod/flutter_riverpod.dart' ;
3+ import 'package:font_awesome_flutter/font_awesome_flutter.dart' ;
54import 'package:url_launcher/url_launcher.dart' ;
65
6+ import '../../../constants/constants.dart' ;
7+ import '../../../constants/style.dart' ;
78import '../../../ui/device.dart' ;
9+ import '../../../ui/widgets/default_card.dart' ;
810
9- class CollaboratorsPage extends ConsumerStatefulWidget {
10- const CollaboratorsPage ({super .key});
11-
12- @override
13- // ignore: library_private_types_in_public_api
14- ConsumerState <CollaboratorsPage > createState () => _CollaboratorsPageState ();
15- }
16-
11+ // [name, role, url (without scheme)]
1712var collaborators = const [
1813 ["Marco Perugini" , "Project Manager" , "github.com/theperu" ],
1914 ["Michele Vulcano" , "Maintainer, Backend Dev" , "github.com/mikev-cw" ],
@@ -29,21 +24,30 @@ var collaborators = const [
2924 ["Alessandro Bongiovanni" , "Flutter Dev" , "github.com/bongio94" ],
3025 [
3126 "Emanuel Passaro" ,
32- "Social Media Manager e Strategist " ,
27+ "Social Media Manager" ,
3328 "linkedin.com/in/emanuelpassaro/" ,
3429 ],
3530 [
3631 "Carolina Verdiani" ,
37- "(digital) Marketing Project Manager" ,
32+ "Marketing Project Manager" ,
3833 "linkedin.com/in/carolina-verdiani/" ,
3934 ],
4035 ["Alessia Schina" , "UX/UI Designer" , "linkedin.com/in/alessiaschina" ],
4136 ["Federico Bruzzone" , "Former Maintainer" , "github.com/FedericoBruzzone" ],
4237];
4338
44- class _CollaboratorsPageState extends ConsumerState <CollaboratorsPage > {
39+ // Cycles through the category colour palette for visual variety.
40+ IconData _platformIcon (String url) {
41+ if (url.contains ('github.com' )) return FontAwesomeIcons .github;
42+ if (url.contains ('linkedin.com' )) return FontAwesomeIcons .linkedin;
43+ return FontAwesomeIcons .globe;
44+ }
45+
46+ class CollaboratorsPage extends ConsumerWidget {
47+ const CollaboratorsPage ({super .key});
48+
4549 @override
46- Widget build (BuildContext context) {
50+ Widget build (BuildContext context, WidgetRef ref ) {
4751 return Scaffold (
4852 appBar: AppBar (
4953 leading: IconButton (
@@ -52,50 +56,148 @@ class _CollaboratorsPageState extends ConsumerState<CollaboratorsPage> {
5256 ),
5357 title: const Text ('Collaborators' ),
5458 ),
55- body: ListView .separated (
56- physics: const BouncingScrollPhysics (),
57- itemCount: collaborators.length,
58- separatorBuilder: (context, index) => const Divider (height: 1 ),
59- itemBuilder: (context, i) {
60- List option = collaborators[i];
61- return InkWell (
62- onTap: () {
63- Uri url = Uri .parse ("https://${option [2 ]}" );
64- launchUrl (url);
65- },
66- child: Padding (
67- padding: const EdgeInsets .all (Sizes .lg),
59+ body: SingleChildScrollView (
60+ padding: const EdgeInsets .only (top: Sizes .xl, bottom: Sizes .xxl),
61+ child: Column (
62+ crossAxisAlignment: CrossAxisAlignment .start,
63+ children: [
64+ // ── Header ─────────────────────────────────────
65+ Padding (
66+ padding: const EdgeInsets .symmetric (horizontal: Sizes .lg),
6867 child: Column (
6968 crossAxisAlignment: CrossAxisAlignment .start,
7069 children: [
7170 Text (
72- option[ 0 ]. toString () ,
73- style: Theme .of (context).textTheme.titleLarge ! .copyWith (
71+ 'Meet the team' ,
72+ style: Theme .of (context).textTheme.headlineMedium ! .copyWith (
7473 color: Theme .of (context).colorScheme.primary,
7574 ),
76- textAlign: TextAlign .left,
7775 ),
78- const SizedBox (height: Sizes .xs ),
76+ const SizedBox (height: Sizes .sm ),
7977 Text (
80- option[ 1 ]. toString () ,
78+ 'sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.' ,
8179 style: Theme .of (context).textTheme.bodyMedium! .copyWith (
82- color: Theme .of (context).colorScheme.primary ,
80+ color: Theme .of (context).colorScheme.outline ,
8381 ),
84- textAlign: TextAlign .left,
8582 ),
86- const SizedBox (height: Sizes .xs),
87- Text (
88- option[2 ].toString (),
89- style: Theme .of (context).textTheme.bodySmall! .copyWith (
90- color: Theme .of (context).colorScheme.primary,
83+ ],
84+ ),
85+ ),
86+ const SizedBox (height: Sizes .xl),
87+
88+ // ── Contributors list ───────────────────────────────────
89+ Column (
90+ spacing: Sizes .sm,
91+ children: List .generate (collaborators.length, (i) {
92+ final c = collaborators[i];
93+ return _ContributorCard (name: c[0 ], role: c[1 ], url: c[2 ]);
94+ }),
95+ ),
96+
97+ const SizedBox (height: Sizes .xxl),
98+
99+ // ── CTA ────────────────────────────────────────────────
100+ DefaultCard (
101+ onTap: () => launchUrl (Uri .parse (githubUrl)),
102+ child: Row (
103+ spacing: Sizes .md,
104+ children: [
105+ Container (
106+ decoration: const BoxDecoration (
107+ color: blue5,
108+ shape: BoxShape .circle,
109+ ),
110+ padding: const EdgeInsets .all (Sizes .md),
111+ child: const FaIcon (
112+ FontAwesomeIcons .github,
113+ color: white,
114+ size: 22 ,
115+ ),
116+ ),
117+ Expanded (
118+ child: Column (
119+ crossAxisAlignment: CrossAxisAlignment .start,
120+ children: [
121+ Text (
122+ 'Want to contribute?' ,
123+ style: Theme .of (context).textTheme.titleLarge!
124+ .copyWith (
125+ color: Theme .of (context).colorScheme.primary,
126+ ),
127+ ),
128+ Text (
129+ 'Open an issue, submit a PR or just say hi on GitHub' ,
130+ style: Theme .of (context).textTheme.bodySmall!
131+ .copyWith (
132+ color: Theme .of (context).colorScheme.outline,
133+ ),
134+ ),
135+ ],
91136 ),
92- textAlign: TextAlign .left,
137+ ),
138+ Icon (
139+ Icons .arrow_forward_ios,
140+ size: 16 ,
141+ color: Theme .of (context).colorScheme.outline,
93142 ),
94143 ],
95144 ),
96145 ),
97- );
98- },
146+ ],
147+ ),
148+ ),
149+ );
150+ }
151+ }
152+
153+ class _ContributorCard extends StatelessWidget {
154+ const _ContributorCard ({
155+ required this .name,
156+ required this .role,
157+ required this .url,
158+ });
159+
160+ final String name;
161+ final String role;
162+ final String url;
163+
164+ @override
165+ Widget build (BuildContext context) {
166+ return DefaultCard (
167+ onTap: () => launchUrl (Uri .parse ('https://$url ' )),
168+ child: Row (
169+ spacing: Sizes .md,
170+ children: [
171+ Expanded (
172+ child: Column (
173+ crossAxisAlignment: CrossAxisAlignment .start,
174+ children: [
175+ Text (
176+ name,
177+ style: Theme .of (context).textTheme.titleSmall! .copyWith (
178+ color: Theme .of (context).colorScheme.primary,
179+ fontWeight: FontWeight .bold,
180+ ),
181+ maxLines: 1 ,
182+ overflow: TextOverflow .ellipsis,
183+ ),
184+ Text (
185+ role,
186+ style: Theme .of (context).textTheme.bodySmall! .copyWith (
187+ color: Theme .of (context).colorScheme.outline,
188+ ),
189+ maxLines: 1 ,
190+ overflow: TextOverflow .ellipsis,
191+ ),
192+ ],
193+ ),
194+ ),
195+ FaIcon (
196+ _platformIcon (url),
197+ size: 14 ,
198+ color: Theme .of (context).colorScheme.outline,
199+ ),
200+ ],
99201 ),
100202 );
101203 }
0 commit comments