ng_higher_ed_institutions 0.0.1
ng_higher_ed_institutions: ^0.0.1 copied to clipboard
A comprehensive Flutter/Dart package providing access to data about all Nigerian higher educational institutions including universities, polytechnics, and colleges.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:ng_higher_ed_institutions/ng_higher_ed_institutions.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Nigerian Higher Education Institutions',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
home: const InstitutionListScreen(),
);
}
}
class InstitutionListScreen extends StatefulWidget {
const InstitutionListScreen({super.key});
@override
State<InstitutionListScreen> createState() => _InstitutionListScreenState();
}
class _InstitutionListScreenState extends State<InstitutionListScreen> {
final TextEditingController _searchController = TextEditingController();
String _selectedState = 'All';
String _selectedFunding = 'All';
List<Institution> _filteredInstitutions = [];
final List<Institution> _allInstitutions =
NigerianInstitutions.getAllInstitutions();
@override
void initState() {
super.initState();
_filteredInstitutions = _allInstitutions;
_searchController.addListener(_filterInstitutions);
}
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
void _filterInstitutions() {
setState(() {
String searchTerm = _searchController.text.toLowerCase();
_filteredInstitutions = _allInstitutions.where((institution) {
bool matchesSearch =
institution.name.toLowerCase().contains(searchTerm) ||
(institution.abbreviation?.toLowerCase().contains(searchTerm) ??
false) ||
institution.state.toLowerCase().contains(searchTerm);
bool matchesState = _selectedState == 'All' ||
institution.state.toLowerCase().replaceAll(' ', '') ==
_selectedState.toLowerCase().replaceAll(' ', '');
bool matchesFunding = _selectedFunding == 'All' ||
institution.funding.name == _selectedFunding.toLowerCase();
return matchesSearch && matchesState && matchesFunding;
}).toList();
});
}
List<String> get _uniqueStates {
final states = _allInstitutions.map((i) => i.state).toSet().toList();
states.sort();
return ['All', ...states];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Nigerian Higher Education Institutions'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
TextField(
controller: _searchController,
decoration: const InputDecoration(
labelText: 'Search institutions',
hintText: 'Search by name, abbreviation, or state',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: DropdownButtonFormField<String>(
initialValue: _selectedState,
decoration: const InputDecoration(
labelText: 'State',
border: OutlineInputBorder(),
),
items: _uniqueStates
.map((state) => DropdownMenuItem(
value: state,
child: Text(state),
))
.toList(),
onChanged: (value) {
setState(() {
_selectedState = value ?? 'All';
});
_filterInstitutions();
},
),
),
const SizedBox(width: 12),
Expanded(
child: DropdownButtonFormField<String>(
initialValue: _selectedFunding,
decoration: const InputDecoration(
labelText: 'Funding',
border: OutlineInputBorder(),
),
items: const [
DropdownMenuItem(value: 'All', child: Text('All')),
DropdownMenuItem(
value: 'federal', child: Text('Federal')),
DropdownMenuItem(
value: 'state', child: Text('State')),
DropdownMenuItem(
value: 'private', child: Text('Private')),
DropdownMenuItem(
value: 'military', child: Text('Military')),
DropdownMenuItem(
value: 'police', child: Text('Police')),
],
onChanged: (value) {
setState(() {
_selectedFunding = value ?? 'All';
});
_filterInstitutions();
},
),
),
],
),
const SizedBox(height: 8),
Text(
'Showing ${_filteredInstitutions.length} of ${_allInstitutions.length} institutions',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
Expanded(
child: _filteredInstitutions.isEmpty
? const Center(
child: Text('No institutions found'),
)
: ListView.builder(
itemCount: _filteredInstitutions.length,
itemBuilder: (context, index) {
final institution = _filteredInstitutions[index];
return Card(
margin: const EdgeInsets.symmetric(
horizontal: 16, vertical: 4),
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).colorScheme.primaryContainer,
child: Text(
institution.name.substring(0, 1).toUpperCase(),
style: TextStyle(
color: Theme.of(context)
.colorScheme
.onPrimaryContainer,
),
),
),
title: Text(institution.name),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${institution.state} • ${institution.lga}',
),
const SizedBox(height: 4),
Wrap(
spacing: 8,
children: [
Chip(
label: Text(
institution.type.name.toUpperCase(),
style: const TextStyle(fontSize: 10),
),
visualDensity: VisualDensity.compact,
),
Chip(
label: Text(
institution.funding.name.toUpperCase(),
style: const TextStyle(fontSize: 10),
),
visualDensity: VisualDensity.compact,
),
if (institution.abbreviation != null)
Chip(
label: Text(
institution.abbreviation!,
style: const TextStyle(fontSize: 10),
),
visualDensity: VisualDensity.compact,
),
],
),
],
),
trailing: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'Est. ${institution.year}',
style: Theme.of(context).textTheme.bodySmall,
),
if (institution.hasWebsite)
const Icon(Icons.link, size: 16),
if (institution.hasCoordinates)
const Icon(Icons.location_on, size: 16),
],
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
InstitutionDetailScreen(institution),
),
);
},
),
);
},
),
),
],
),
);
}
}
class InstitutionDetailScreen extends StatelessWidget {
final Institution institution;
const InstitutionDetailScreen(this.institution, {super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(institution.name),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16.0),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
institution.name,
style: Theme.of(context).textTheme.headlineSmall,
),
if (institution.abbreviation != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text(
'Abbreviation: ${institution.abbreviation}',
style: Theme.of(context).textTheme.titleMedium,
),
),
const Divider(),
_buildDetailRow('State', institution.state),
_buildDetailRow('LGA', institution.lga),
_buildDetailRow('Type', institution.type.name),
_buildDetailRow('Funding', institution.funding.name),
_buildDetailRow('Established', institution.year.toString()),
if (institution.hasWebsite)
_buildDetailRow('Website', institution.website!),
if (institution.hasCoordinates)
_buildDetailRow(
'Coordinates',
'${institution.latitude}, ${institution.longitude}',
),
],
),
),
),
const SizedBox(height: 16),
Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Data Availability',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 8),
_buildAvailabilityRow(
'Website',
institution.hasWebsite,
),
_buildAvailabilityRow(
'Coordinates',
institution.hasCoordinates,
),
_buildAvailabilityRow(
'Abbreviation',
institution.abbreviation != null,
),
],
),
),
),
],
),
);
}
Widget _buildDetailRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 100,
child: Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.bold),
),
),
Expanded(
child: Text(value),
),
],
),
);
}
Widget _buildAvailabilityRow(String label, bool available) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Icon(
available ? Icons.check_circle : Icons.cancel,
color: available ? Colors.green : Colors.grey,
size: 20,
),
const SizedBox(width: 8),
Text(label),
],
),
);
}
}