From efccfd05e9b19cf3bcf664799bea8136c682d666 Mon Sep 17 00:00:00 2001 From: Saumya-28 Date: Sun, 26 May 2024 17:07:59 +0530 Subject: [PATCH 1/4] Implemented Multi-Select Filter Feature for Projects and Organizations --- lib/programs screen/girl_script.dart | 513 +++++++++-------- .../google_season_of_docs_screen.dart | 523 +++++++++++------- .../google_summer_of_code_screen.dart | 153 ++--- 3 files changed, 669 insertions(+), 520 deletions(-) diff --git a/lib/programs screen/girl_script.dart b/lib/programs screen/girl_script.dart index 8ea149d..da361be 100644 --- a/lib/programs screen/girl_script.dart +++ b/lib/programs screen/girl_script.dart @@ -3,12 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:opso/modals/book_mark_model.dart'; import 'package:opso/modals/gssoc_project_modal.dart'; - - import 'package:opso/widgets/gssoc_project_widget.dart'; import 'package:opso/widgets/year_button.dart'; - - import '../widgets/SearchandFilterWidget.dart'; @@ -31,6 +27,7 @@ class _GSSOCScreenState extends State { List gssoc2021 = []; int selectedYear = 2024; String selectedOrg = "All"; + String selectedLanguage = "All"; List projectList = []; Future? getProjectFunction; @@ -80,15 +77,14 @@ class _GSSOCScreenState extends State { } - void searchTag(String searchTag) { - projectList = projectList - .where((element) => element.techstack.contains(searchTag)) - .toList(); + void filterProjectsByTag(String tag) { + projectList = projectList.where((element) => element.techstack.contains(tag)).toList(); + _resetOrgIfNotValid(); setState(() {}); } - void search(String searchText) { + void filterProjectsBySearchText(String searchText) { if (searchText.isEmpty) { switch (selectedYear) { case 2021: @@ -104,6 +100,7 @@ class _GSSOCScreenState extends State { projectList = gssoc2024; break; } + _resetOrgIfNotValid(); setState(() {}); return; } @@ -115,14 +112,49 @@ class _GSSOCScreenState extends State { element.hostedBy.toLowerCase().contains(searchText.toLowerCase()), ) .toList(); + _resetOrgIfNotValid(); setState(() {}); } + void resetProjectsByLanguage() { + switch (selectedYear) { + case 2021: + projectList = gssoc2021; + break; + case 2022: + projectList = gssoc2022; + break; + case 2023: + projectList = gssoc2023; + break; + case 2024: + projectList = gssoc2024; + break; + } + if (selectedLanguage != 'All') { + filterProjectsByTag(selectedLanguage); + } else { + _resetOrgIfNotValid(); + setState(() {}); + } + } + + + void _resetOrgIfNotValid() { + Set uniqueOrgs = projectList.map((e) => e.hostedBy).toSet(); + if (!uniqueOrgs.contains(selectedOrg) && selectedOrg != 'All') { + selectedOrg = 'All'; + } + } + + Future _refresh() async { setState(() { initializeProjectLists(); selectedYear = 2024; + selectedOrg = 'All'; + selectedLanguage = 'All'; }); } @@ -150,257 +182,252 @@ class _GSSOCScreenState extends State { 'Dart' ]; return RefreshIndicator( - onRefresh: _refresh, - child: Scaffold( - appBar: AppBar(title: const Text('GSSoC'), actions: [ - IconButton( - icon: (isBookmarked) - ? const Icon(Icons.bookmark_add_rounded) - : const Icon(Icons.bookmark_add_outlined), - onPressed: () { - setState(() { - isBookmarked = !isBookmarked; - }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - isBookmarked ? 'Bookmark added' : 'Bookmark removed'), - duration: const Duration( - seconds: 2), // Adjust the duration as needed - ), - ); - if (isBookmarked) { - print("Adding"); - HandleBookmark.addBookmark(currentProject, currectPage); - } else { - print("Deleting"); - HandleBookmark.deleteBookmark(currentProject); + onRefresh: _refresh, + child: Scaffold( + appBar: AppBar(title: const Text('GSSoC'), actions: [ + IconButton( + icon: (isBookmarked) + ? const Icon(Icons.bookmark_add_rounded) + : const Icon(Icons.bookmark_add_outlined), + onPressed: () { + setState(() { + isBookmarked = !isBookmarked; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + isBookmarked ? 'Bookmark added' : 'Bookmark removed'), + duration: const Duration( + seconds: 2), // Adjust the duration as needed + ), + ); + if (isBookmarked) { + print("Adding"); + HandleBookmark.addBookmark(currentProject, currectPage); + } else { + print("Deleting"); + HandleBookmark.deleteBookmark(currentProject); + } + }, + ) + ]), + body: FutureBuilder( + future: getProjectFunction, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.connectionState == ConnectionState.done) { + // Create a unique list of organizations + Set uniqueOrgs = projectList.map((e) => e.hostedBy).toSet(); + List orgsList = ["All", ...uniqueOrgs]; + + + // Reset selectedOrg if it's not in the filtered list + if (!orgsList.contains(selectedOrg)) { + selectedOrg = 'All'; } - }, - ) - ]), - body: FutureBuilder( - future: getProjectFunction, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.connectionState == ConnectionState.done) { - return Padding( - padding: - const EdgeInsets.symmetric(horizontal: 46, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - decoration: InputDecoration( - filled: true, - // fillColor: const Color(0xFFEEEEEE), - hintText: 'Search', - suffixIcon: const Icon(Icons.search), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + + + return Padding( + padding: + const EdgeInsets.symmetric(horizontal: 46, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextFormField( + decoration: InputDecoration( + filled: true, + hintText: 'Search', + suffixIcon: const Icon(Icons.search), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - contentPadding: const EdgeInsets.symmetric( - vertical: 12.0, horizontal: 20.0), ), - onFieldSubmitted: (value) { - print("value is $value"); - search(value.trim()); - }, - onChanged: (value) { - if (value.isEmpty) { - search(value); - } - }, + contentPadding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 20.0), ), - const SizedBox(height: 20), - SizedBox( - height: height * 0.2, - width: width, - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( + onFieldSubmitted: (value) { + print("value is $value"); + filterProjectsBySearchText(value.trim()); + }, + onChanged: (value) { + if (value.isEmpty) { + filterProjectsBySearchText(value); + } + }, + ), + const SizedBox(height: 20), + SizedBox( + height: height * 0.2, + width: width, + child: GridView( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 1.5/0.6, crossAxisCount: 2, - childAspectRatio: 1.5 / 0.6, crossAxisSpacing: 15, - mainAxisSpacing: 15, - ), - children: [ - YearButton( - year: "2021", - isEnabled: selectedYear == 2021 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2021; - selectedYear = 2021; - }); - }, - backgroundColor: selectedYear == 2021 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2022", - isEnabled: selectedYear == 2022 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2022; - selectedYear = 2022; - }); - }, - backgroundColor: selectedYear == 2022 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2023", - isEnabled: selectedYear == 2023 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2023; - selectedYear = 2023; - }); - }, - backgroundColor: selectedYear == 2023 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - isEnabled: selectedYear == 2024 ? true : false, - year: "2024", - onTap: () { - setState(() { - projectList = gssoc2024; - selectedYear = 2024; - }); - }, - backgroundColor: selectedYear == 2024 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - ], - ), - ), - // const SizedBox(height: 20), - // SizedBox( - // height: 50, - // child: ElevatedButton( - // onPressed: () {}, - // style: ElevatedButton.styleFrom( - // shape: const RoundedRectangleBorder( - // borderRadius: BorderRadius.zero, - // ), - // backgroundColor: const Color.fromARGB( - // 255, 253, 214, 115), // Set button color - // padding: const EdgeInsets.symmetric( - // vertical: 10.0, horizontal: 20.0), - // ), - // child: const Text( - // 'View Projects', - // style: TextStyle(color: Colors.white, fontSize: 18), - // ), - // ), - // ), - const SizedBox( - height: 20, - ), - - - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSpacing: 15), children: [ - const Text( - 'Filter by Language:', - style: TextStyle(fontWeight: FontWeight.w400), + YearButton( + year: "2021", + isEnabled: selectedYear == 2021 ? true : false, + onTap: () { + setState(() { + projectList = gssoc2021; + selectedYear = 2021; + }); + resetProjectsByLanguage(); + }, + backgroundColor: selectedYear == 2021 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2022", + isEnabled: selectedYear == 2022 ? true : false, + onTap: () { + setState(() { + projectList = gssoc2022; + selectedYear = 2022; + }); + resetProjectsByLanguage(); + }, + backgroundColor: selectedYear == 2022 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2023", + isEnabled: selectedYear == 2023 ? true : false, + onTap: () { + setState(() { + projectList = gssoc2023; + selectedYear = 2023; + }); + resetProjectsByLanguage(); + }, + backgroundColor: selectedYear == 2023 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + isEnabled: selectedYear == 2024 ? true : false, + year: "2024", + onTap: () { + setState(() { + projectList = gssoc2024; + selectedYear = 2024; + }); + resetProjectsByLanguage(); + }, + backgroundColor: selectedYear == 2024 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - DropdownWidget( - items: languages, - hintText: 'Language', - onChanged: (newValue) { - setState(() { - switch (selectedYear) { - case 2021: - projectList = gssoc2021; - break; - case 2022: - projectList = gssoc2022; - break; - case 2023: - projectList = gssoc2023; - break; - case 2024: - projectList = gssoc2024; - break; - } - searchTag(newValue); - }); - // Perform filtering based on selectedLanguage - }, - ), - ], - ), - ) ], ), - - - Expanded( - // width: width, - child: ListView.builder( - itemCount: projectList.length, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: GssocProjectWidget( - index: index + 1, - modal: projectList[index], - height: height * 0.2, - width: width, - ), - ); + ), + const SizedBox(height: 20), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Filter by Language:', + style: TextStyle(fontWeight: FontWeight.w400), + ), + const SizedBox(width: 8), + DropdownWidget( + items: languages, + hintText: 'Language', + onChanged: (newValue) { + setState(() { + selectedLanguage = newValue; + resetProjectsByLanguage(); + }); }, ), + ], + ), + const SizedBox(height: 20), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Filter by Name:', + style: TextStyle(fontWeight: FontWeight.w400), + ), + const SizedBox(width: 8), + Expanded( + child: DropdownButton( + value: selectedOrg, + hint: const Text('Organization'), + isExpanded: true, + items: orgsList + .map((org) => DropdownMenuItem( + child: Text(org), + value: org, + )) + .toList(), + onChanged: (newValue) { + setState(() { + selectedOrg = newValue!; + resetProjectsByLanguage(); + if (selectedOrg != 'All') { + filterProjectsBySearchText(selectedOrg); + } + }); + }, + ), + ), + ], + ), + const SizedBox(height: 20), + Expanded( + child: ListView.builder( + itemCount: projectList.length, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: GssocProjectWidget( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ), + ); + }, ), - ], - ), - ); - } else { - return const Center(child: Text("Some error occured")); - } - }), - ) + ), + ], + ), + ); + } else { + return const Center(child: Text("Some error occurred")); + } + }), + ), ); } } - - diff --git a/lib/programs screen/google_season_of_docs_screen.dart b/lib/programs screen/google_season_of_docs_screen.dart index 6e2df0e..4039687 100644 --- a/lib/programs screen/google_season_of_docs_screen.dart +++ b/lib/programs screen/google_season_of_docs_screen.dart @@ -19,6 +19,8 @@ class GoogleSeasonOfDocsScreen extends StatefulWidget { class _GoogleSeasonOfDocsScreenState extends State { + String selectedOrganization = 'All'; + String selectedProposal = 'All'; String currentProgram = "Google Season of Docs"; bool isBookmarked = true; String currentPage = "/google_season_of_docs"; @@ -96,24 +98,7 @@ class _GoogleSeasonOfDocsScreenState extends State { void search(String searchText) { if (searchText.isEmpty) { - switch (selectedYear) { - case 2021: - projectList = gsod2021; - break; - case 2022: - projectList = gsod2022; - break; - case 2023: - projectList = gsod2023; - break; - case 20202: - projectList = gsod2020; - break; - case 2019: - projectList = gsod2019; - break; - } - setState(() {}); + resetProjectsByLanguage(); return; } if (selectedYear > 2020) { @@ -162,16 +147,84 @@ class _GoogleSeasonOfDocsScreenState extends State { } - setState(() {}); + setState(() { + _resetValueIfNotValid(); + }); } + + Future _refresh() async { // Fetch data for the next year based on the currently selected year setState(() { initializeProjectLists(); + selectedOrganization = 'All'; + selectedProposal = 'All'; selectedYear = 2023; if (selectedYear > 2023) selectedYear = 2019; // Reset to the beginning if it exceeds 2023 }); } + + + void resetProjectsByLanguage() { + switch (selectedYear) { + case 2019: + projectList = gsod2019; + break; + case 2020: + projectList = gsod2020; + break; + case 2021: + projectList = gsod2021; + break; + case 2022: + projectList = gsod2022; + break; + case 2023: + projectList = gsod2023; + break; + case 2024: + projectList = gsod2023; + break; + } + if (selectedOrganization != 'All') { + filterProjectsByTag(selectedOrganization); + } + setState(() { + _resetValueIfNotValid(); + }); + } + + + void filterProjectsByTag(String tag) { + projectList = projectList + .where((element) => element.tags.contains(tag)) + .toList(); + setState(() { + _resetValueIfNotValid(); + }); + } + + + void _resetValueIfNotValid() { + // Reset selectedOrganization if it is not valid + var validOrganizations = projectList + .map((project) => project.organizationName) + .toSet(); // Use a Set to ensure uniqueness + if (!validOrganizations.contains(selectedOrganization)) { + selectedOrganization = 'All'; + } + + + // Reset selectedProposal if it is not valid + var validProposals = projectList + .map((project) => project.acceptedProjectProposal) + .toSet(); // Use a Set to ensure uniqueness + if (!validProposals.contains(selectedProposal)) { + selectedProposal = 'All'; + } + } + + @override Widget build(BuildContext context) { var height = MediaQuery.sizeOf(context).height; @@ -190,10 +243,9 @@ class _GoogleSeasonOfDocsScreenState extends State { }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text(isBookmarked ? 'Bookmark added' : 'Bookmark removed'), - duration: - const Duration(seconds: 2), // Adjust the duration as needed + content: Text( + isBookmarked ? 'Bookmark added' : 'Bookmark removed'), + duration: const Duration(seconds: 2), // Adjust the duration as needed ), ); if (isBookmarked) { @@ -207,200 +259,261 @@ class _GoogleSeasonOfDocsScreenState extends State { ) ]), body: FutureBuilder( - future: getProjectFunction, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.connectionState == ConnectionState.done) { - return Padding( - padding: - const EdgeInsets.symmetric(horizontal: 46, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - decoration: InputDecoration( - filled: true, - // fillColor: const Color(0xFFEEEEEE), - hintText: 'Search', - suffixIcon: const Icon(Icons.search), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + future: getProjectFunction, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.connectionState == ConnectionState.done) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 46, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextFormField( + decoration: InputDecoration( + filled: true, + // fillColor: const Color(0xFFEEEEEE), + hintText: 'Search', + suffixIcon: const Icon(Icons.search), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), ), - contentPadding: const EdgeInsets.symmetric( - vertical: 12.0, horizontal: 20.0), ), - onFieldSubmitted: (value) { - print("value is $value"); - search(value.trim()); - }, - onChanged: (value) { - if (value.isEmpty) { - search(value); - } - }, + contentPadding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 20.0), ), - const SizedBox(height: 20), - Container( - constraints: BoxConstraints( - maxHeight: height * 0.3, + onFieldSubmitted: (value) { + print("value is $value"); + search(value.trim()); + }, + onChanged: (value) { + if (value.isEmpty) { + search(value); + } + }, + ), + const SizedBox(height: 20), + Container( + constraints: BoxConstraints( + maxHeight: height * 0.3, + ), + width: width, + child: GridView( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 1.5 / 0.6, + crossAxisSpacing: 15, + mainAxisSpacing: 15, ), - width: width, - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: 1.5 / 0.6, - crossAxisSpacing: 15, - mainAxisSpacing: 15, + children: [ + YearButton( + year: "2019", + isEnabled: selectedYear == 2019 ? true : false, + onTap: () { + setState(() { + projectList = gsod2019; + selectedYear = 2019; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2019 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), ), - children: [ - YearButton( - year: "2019", - isEnabled: selectedYear == 2019 ? true : false, - onTap: () { - setState(() { - projectList = gsod2019; - selectedYear = 2019; - }); - }, - backgroundColor: selectedYear == 2019 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2020", - isEnabled: selectedYear == 2020 ? true : false, - onTap: () { - setState(() { - projectList = gsod2020; - selectedYear = 2020; - }); - }, - backgroundColor: selectedYear == 2020 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2021", - isEnabled: selectedYear == 2021 ? true : false, - onTap: () { - setState(() { - projectList = gsod2021; - selectedYear = 2021; - }); - }, - backgroundColor: selectedYear == 2021 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2022", - isEnabled: selectedYear == 2022 ? true : false, - onTap: () { - setState(() { - projectList = gsod2022; - selectedYear = 2022; - }); - }, - backgroundColor: selectedYear == 2022 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2023", - isEnabled: selectedYear == 2023 ? true : false, - onTap: () { - setState(() { - projectList = gsod2023; - selectedYear = 2023; - }); - }, - backgroundColor: selectedYear == 2023 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - ], - ), - ), - // const SizedBox(height: 20), - // SizedBox( - // height: 50, - // child: ElevatedButton( - // onPressed: () {}, - // style: ElevatedButton.styleFrom( - // shape: const RoundedRectangleBorder( - // borderRadius: BorderRadius.zero, - // ), - // backgroundColor: const Color.fromARGB( - // 255, 253, 214, 115), // Set button color - // padding: const EdgeInsets.symmetric( - // vertical: 10.0, horizontal: 20.0), - // ), - // child: const Text( - // 'View Projects', - // style: TextStyle(color: Colors.white, fontSize: 18), - // ), - // ), - // ), - const SizedBox( - height: 20, + YearButton( + year: "2020", + isEnabled: selectedYear == 2020 ? true : false, + onTap: () { + setState(() { + projectList = gsod2020; + selectedYear = 2020; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2020 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2021", + isEnabled: selectedYear == 2021 ? true : false, + onTap: () { + setState(() { + projectList = gsod2021; + selectedYear = 2021; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2021 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2022", + isEnabled: selectedYear == 2022 ? true : false, + onTap: () { + setState(() { + projectList = gsod2022; + selectedYear = 2022; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2022 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2023", + isEnabled: selectedYear == 2023 ? true : false, + onTap: () { + setState(() { + projectList = gsod2023; + selectedYear = 2023; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2023 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + ], ), - - - Expanded( - // width: width, - child: ListView.builder( - itemCount: projectList.length, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: selectedYear <= 2020 - ? GsodProjectWidgetOld( - index: index + 1, - modal: projectList[index], - height: height * 0.2, - width: width, - ) - : GsodProjectWidgetNew( - index: index + 1, - modal: projectList[index], - height: height * 0.2, - width: width, + ), + const SizedBox( + height: 20, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Filter by Organization:', + style: TextStyle(fontWeight: FontWeight.w400), + ), + const SizedBox(width: 8), + Expanded( + child: DropdownButton( + value: selectedOrganization, + hint: const Text('Organization'), + isExpanded: true, + items: [ + DropdownMenuItem( + value: 'All', // Unique value for "All" + child: const Text('All'), ), - ); - }, + ...projectList + .map((project) => project.organizationName) + .toSet() // Use a Set to ensure uniqueness + .map((organization) { + return DropdownMenuItem( + value: organization, + child: Text(organization), + ); + }).toList(), + ], + onChanged: (newValue) { + setState(() { + selectedOrganization = newValue!; + search(newValue); + }); + }, + ), + ), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Filter by Accepted Proposal:', + style: TextStyle(fontWeight: FontWeight.w400), ), + const SizedBox(width: 8), + Expanded( + child: DropdownButton( + value: selectedProposal, + hint: const Text('Proposal'), + isExpanded: true, + items: [ + DropdownMenuItem( + value: 'All', // Unique value for "All" + child: const Text('All'), + ), + ...projectList + .map((project) => + project.acceptedProjectProposal) + .toSet() // Use a Set to ensure uniqueness + .toList() + .map((proposal) { + return DropdownMenuItem( + value: proposal, + child: Text(proposal), + ); + }).toList(), + ], + onChanged: (newValue) { + setState(() { + selectedProposal = newValue!; + search(newValue); + }); + }, + ), + ), + ], + ), + const SizedBox(height: 20), + Expanded( + child: ListView.builder( + itemCount: projectList.length, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: selectedYear <= 2020 + ? GsodProjectWidgetOld( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ) + : GsodProjectWidgetNew( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ), + ); + }, ), - ], - ), - ); - } else { - return const Center(child: Text("Some error occured")); - } - }), + ), + ], + ), + ); + } else { + return const Center(child: Text("Some error occurred")); + } + }, + ), ), ); } diff --git a/lib/programs screen/google_summer_of_code_screen.dart b/lib/programs screen/google_summer_of_code_screen.dart index d324216..1329e69 100644 --- a/lib/programs screen/google_summer_of_code_screen.dart +++ b/lib/programs screen/google_summer_of_code_screen.dart @@ -15,6 +15,7 @@ class GoogleSummerOfCodeScreen extends StatefulWidget { class _GoogleSummerOfCodeScreenState extends State { + String selectedOrg = 'All'; List gsoc2024 = []; List gsoc2023 = []; List gsoc2022 = []; @@ -39,7 +40,6 @@ class _GoogleSummerOfCodeScreenState extends State { ]; List orgList = []; late Future _dataFetchFuture; - final GlobalKey _listKey = GlobalKey(); @override @@ -65,45 +65,25 @@ class _GoogleSummerOfCodeScreenState extends State { gsoc2024 = orgData2024.organizations ?? []; orgList = gsoc2024; // Default to the latest year }); - _populateAnimatedList(orgList); } catch (e) { print('Error: $e'); } } - void _clearAnimatedList() { - for (var i = orgList.length - 1; i >= 0; i--) { - _listKey.currentState?.removeItem( - i, - (context, animation) => SizedBox.shrink(), - duration: Duration.zero, - ); - } - } - - - void _populateAnimatedList(List organizations) { - for (var i = 0; i < organizations.length; i++) { - _listKey.currentState?.insertItem(i); - } - } - - void searchTag(String searchTag) { setState(() { - _clearAnimatedList(); + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch orgList = _getOrganizationsByYear(selectedYear) .where((element) => element.technologies?.contains(searchTag) == true || element.topics?.contains(searchTag) == true) .toList(); - _populateAnimatedList(orgList); }); } void search(String searchText) { setState(() { - _clearAnimatedList(); + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch if (searchText.isEmpty) { orgList = _getOrganizationsByYear(selectedYear); } else { @@ -111,7 +91,6 @@ class _GoogleSummerOfCodeScreenState extends State { .where((element) => element.name?.toLowerCase().contains(searchText.toLowerCase()) == true) .toList(); } - _populateAnimatedList(orgList); }); } @@ -136,6 +115,20 @@ class _GoogleSummerOfCodeScreenState extends State { setState(() { _dataFetchFuture = getProjectData(); selectedYear = 2024; + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch + }); + } + + + void resetProjectsByOrganization() { + setState(() { + if (selectedOrg == 'All') { + orgList = _getOrganizationsByYear(selectedYear); + } else { + orgList = _getOrganizationsByYear(selectedYear) + .where((element) => element.name == selectedOrg) + .toList(); + } }); } @@ -226,10 +219,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2021; - _clearAnimatedList(); + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch orgList = gsoc2021; }); - _populateAnimatedList(gsoc2021); }, backgroundColor: selectedYear == 2021 ? Colors.white @@ -241,10 +233,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2022; - _clearAnimatedList(); + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch orgList = gsoc2022; }); - _populateAnimatedList(gsoc2022); }, backgroundColor: selectedYear == 2022 ? Colors.white @@ -256,10 +247,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2023; - _clearAnimatedList(); + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch orgList = gsoc2023; }); - _populateAnimatedList(gsoc2023); }, backgroundColor: selectedYear == 2023 ? Colors.white @@ -271,10 +261,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2024; - _clearAnimatedList(); + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch orgList = gsoc2024; }); - _populateAnimatedList(gsoc2024); }, backgroundColor: selectedYear == 2024 ? Colors.white @@ -309,12 +298,67 @@ class _GoogleSummerOfCodeScreenState extends State { ) ], ), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text( + 'Filter by Organization:', + style: TextStyle(fontWeight: FontWeight.w400), + ), + const SizedBox(width: 8), + Expanded( + child: DropdownButton( + value: selectedOrg, + hint: const Text('Organization'), + isExpanded: true, + items: [ + 'All', + if (orgList.isNotEmpty) + ...orgList.map((org) => org.name!).toList() + ].map((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + onChanged: (newValue) { + setState(() { + selectedOrg = newValue!; + resetProjectsByOrganization(); + }); + }, + ), + ), + ], + ), + const SizedBox(height: 20), Expanded( - child: AnimatedList( - key: _listKey, - initialItemCount: orgList.length, - itemBuilder: (context, index, animation) { - return _buildAnimatedItem(context, index, animation, height, width); + child: ListView.builder( + itemCount: orgList.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), // changes position of shadow + ), + ], + ), + child: GsocProjectWidget( + index: index + 1, + modal: orgList[index], + height: height * 0.2, + width: width, + ), + ), + ); }, ), ), @@ -327,41 +371,6 @@ class _GoogleSummerOfCodeScreenState extends State { ), ); } - - - Widget _buildAnimatedItem(BuildContext context, int index, Animation animation, double height, double width) { - return SizeTransition( - sizeFactor: animation, - axis: Axis.vertical, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), // changes position of shadow - ), - ], - ), - child: Column( - children: [ - GsocProjectWidget( - index: index + 1, - modal: orgList[index], - height: height * 0.2, - width: width, - ), - ], - ), - ), - ), - ); - } } From 3dbc110566c0472ac1a25948151a127fa3531992 Mon Sep 17 00:00:00 2001 From: Saumya-28 Date: Wed, 29 May 2024 00:19:29 +0530 Subject: [PATCH 2/4] Updated Multi-Select Filter Feature for Projects and Organizations --- lib/programs screen/girl_script.dart | 625 +++++++++--------- .../google_season_of_docs_screen.dart | 531 ++++++++------- .../google_summer_of_code_screen.dart | 187 +++--- pubspec.lock | 8 + pubspec.yaml | 2 +- 5 files changed, 664 insertions(+), 689 deletions(-) diff --git a/lib/programs screen/girl_script.dart b/lib/programs screen/girl_script.dart index da361be..9be23d7 100644 --- a/lib/programs screen/girl_script.dart +++ b/lib/programs screen/girl_script.dart @@ -8,6 +8,9 @@ import 'package:opso/widgets/year_button.dart'; import '../widgets/SearchandFilterWidget.dart'; +import 'package:multi_select_flutter/multi_select_flutter.dart'; + + class GSSOCScreen extends StatefulWidget { const GSSOCScreen({super.key}); @@ -18,53 +21,89 @@ class GSSOCScreen extends StatefulWidget { class _GSSOCScreenState extends State { - String currectPage = "/girl_script_summer_of_code"; + String currentPage = "/girl_script_summer_of_code"; String currentProject = "Girl Script Summer of Code"; List gssoc2024 = []; List gssoc2023 = []; - bool isBookmarked = true; List gssoc2022 = []; List gssoc2021 = []; + List allOrganizations = []; + List allLanguages = []; + List selectedOrganizations = ['All']; + List selectedLanguages = ['All']; int selectedYear = 2024; - String selectedOrg = "All"; - String selectedLanguage = "All"; + bool isBookmarked = true; List projectList = []; Future? getProjectFunction; Future initializeProjectLists() async { - String response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2024.json'); - var jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2024.add(GssocProjectModal.getDataFromJson(data)); - } - projectList = List.from(gssoc2024); - response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2023.json'); - jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2023.add(GssocProjectModal.getDataFromJson(data)); - } - response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2022.json'); - jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2022.add(GssocProjectModal.getDataFromJson(data)); - } - response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2021.json'); - jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2021.add(GssocProjectModal.getDataFromJson(data)); - } + await _loadProjects('assets/projects/gssoc/gssoc2024.json', gssoc2024); + await _loadProjects('assets/projects/gssoc/gssoc2023.json', gssoc2023); + await _loadProjects('assets/projects/gssoc/gssoc2022.json', gssoc2022); + await _loadProjects('assets/projects/gssoc/gssoc2021.json', gssoc2021); + + + // Populate all unique organizations and languages + allOrganizations = _extractUniqueValues((project) => project.hostedBy); + allLanguages = languages; + projectList = List.from(gssoc2024); // Default year + } + + + List languages = [ + 'All', + 'Js', + 'Python', + 'React', + 'Angular', + 'Bootstrap', + 'Firebase', + 'Node', + 'MongoDb', + 'Express', + 'Next', + 'CSS', + 'HTML', + 'JavaScript', + 'Flutter', + 'Dart' + ]; + + + Future _loadProjects(String path, List list) async { + String response = await rootBundle.loadString(path); + var jsonList = json.decode(response) as List; + list.addAll(jsonList.map((data) => GssocProjectModal.getDataFromJson(data)).toList()); + } + + + List _extractUniqueValues(String Function(GssocProjectModal) extractor) { + return { + 'All', + ...gssoc2024.map(extractor), + ...gssoc2023.map(extractor), + ...gssoc2022.map(extractor), + ...gssoc2021.map(extractor), + }.toList(); + } + + + List _extractUniqueLanguages(List Function(GssocProjectModal) extractor) { + final allLanguages = [ + for (var project in gssoc2024) ...extractor(project), + for (var project in gssoc2023) ...extractor(project), + for (var project in gssoc2022) ...extractor(project), + for (var project in gssoc2021) ...extractor(project), + ]; + return ['All', ...allLanguages.toSet()]; } @override void initState() { - getProjectFunction = initializeProjectLists(); super.initState(); + getProjectFunction = initializeProjectLists(); _checkBookmarkStatus(); } @@ -77,84 +116,53 @@ class _GSSOCScreenState extends State { } - void filterProjectsByTag(String tag) { - projectList = projectList.where((element) => element.techstack.contains(tag)).toList(); - _resetOrgIfNotValid(); - setState(() {}); - } + void filterProjects() { + projectList = _getProjectsByYear(); + + + if (!selectedLanguages.contains('All')) { + projectList = projectList.where((project) => project.techstack.any(selectedLanguages.contains)).toList(); + } - void filterProjectsBySearchText(String searchText) { - if (searchText.isEmpty) { - switch (selectedYear) { - case 2021: - projectList = gssoc2021; - break; - case 2022: - projectList = gssoc2022; - break; - case 2023: - projectList = gssoc2023; - break; - case 2024: - projectList = gssoc2024; - break; - } - _resetOrgIfNotValid(); - setState(() {}); - return; + if (!selectedOrganizations.contains('All')) { + projectList = projectList.where((project) => selectedOrganizations.contains(project.hostedBy)).toList(); } - projectList = projectList - .where( - (element) => - element.name.toLowerCase().contains(searchText.toLowerCase()) || - element.techstack.contains(searchText) || - element.hostedBy.toLowerCase().contains(searchText.toLowerCase()), - ) + + + // Update organization filter based on selected languages + allOrganizations = _extractUniqueValues((project) => project.hostedBy) + .where((organization) => projectList.any((project) => project.hostedBy == organization)) .toList(); - _resetOrgIfNotValid(); + allOrganizations.insert(0, 'All'); + + setState(() {}); } - void resetProjectsByLanguage() { + List _getProjectsByYear() { switch (selectedYear) { case 2021: - projectList = gssoc2021; - break; + return gssoc2021; case 2022: - projectList = gssoc2022; - break; + return gssoc2022; case 2023: - projectList = gssoc2023; - break; + return gssoc2023; case 2024: - projectList = gssoc2024; - break; - } - if (selectedLanguage != 'All') { - filterProjectsByTag(selectedLanguage); - } else { - _resetOrgIfNotValid(); - setState(() {}); - } - } - - - void _resetOrgIfNotValid() { - Set uniqueOrgs = projectList.map((e) => e.hostedBy).toSet(); - if (!uniqueOrgs.contains(selectedOrg) && selectedOrg != 'All') { - selectedOrg = 'All'; + return gssoc2024; + default: + return []; } } Future _refresh() async { + await initializeProjectLists(); setState(() { - initializeProjectLists(); selectedYear = 2024; - selectedOrg = 'All'; - selectedLanguage = 'All'; + selectedOrganizations = ['All']; + selectedLanguages = ['All']; }); } @@ -163,24 +171,8 @@ class _GSSOCScreenState extends State { Widget build(BuildContext context) { var height = MediaQuery.sizeOf(context).height; var width = MediaQuery.sizeOf(context).width; - List languages = [ - 'All', - 'Js', - 'Python', - 'React', - 'Angular', - 'Bootstrap', - 'Firebase', - 'Node', - 'MongoDb', - 'Express', - 'Next', - 'CSS', - 'HTML', - 'JavaScript', - 'Flutter', - 'Dart' - ]; + + return RefreshIndicator( onRefresh: _refresh, child: Scaffold( @@ -195,237 +187,216 @@ class _GSSOCScreenState extends State { }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - isBookmarked ? 'Bookmark added' : 'Bookmark removed'), - duration: const Duration( - seconds: 2), // Adjust the duration as needed + content: Text(isBookmarked ? 'Bookmark added' : 'Bookmark removed'), + duration: const Duration(seconds: 2), ), ); if (isBookmarked) { - print("Adding"); - HandleBookmark.addBookmark(currentProject, currectPage); + HandleBookmark.addBookmark(currentProject, currentPage); } else { - print("Deleting"); HandleBookmark.deleteBookmark(currentProject); } }, ) ]), body: FutureBuilder( - future: getProjectFunction, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.connectionState == ConnectionState.done) { - // Create a unique list of organizations - Set uniqueOrgs = projectList.map((e) => e.hostedBy).toSet(); - List orgsList = ["All", ...uniqueOrgs]; - - - // Reset selectedOrg if it's not in the filtered list - if (!orgsList.contains(selectedOrg)) { - selectedOrg = 'All'; - } - - - return Padding( - padding: - const EdgeInsets.symmetric(horizontal: 46, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - decoration: InputDecoration( - filled: true, - hintText: 'Search', - suffixIcon: const Icon(Icons.search), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - contentPadding: const EdgeInsets.symmetric( - vertical: 12.0, horizontal: 20.0), - ), - onFieldSubmitted: (value) { - print("value is $value"); - filterProjectsBySearchText(value.trim()); - }, - onChanged: (value) { - if (value.isEmpty) { - filterProjectsBySearchText(value); - } - }, - ), - const SizedBox(height: 20), - SizedBox( - height: height * 0.2, - width: width, - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - childAspectRatio: 1.5/0.6, - crossAxisCount: 2, - crossAxisSpacing: 15, - mainAxisSpacing: 15), - children: [ - YearButton( - year: "2021", - isEnabled: selectedYear == 2021 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2021; - selectedYear = 2021; - }); - resetProjectsByLanguage(); - }, - backgroundColor: selectedYear == 2021 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2022", - isEnabled: selectedYear == 2022 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2022; - selectedYear = 2022; - }); - resetProjectsByLanguage(); - }, - backgroundColor: selectedYear == 2022 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2023", - isEnabled: selectedYear == 2023 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2023; - selectedYear = 2023; - }); - resetProjectsByLanguage(); - }, - backgroundColor: selectedYear == 2023 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - isEnabled: selectedYear == 2024 ? true : false, - year: "2024", - onTap: () { - setState(() { - projectList = gssoc2024; - selectedYear = 2024; - }); - resetProjectsByLanguage(); - }, - backgroundColor: selectedYear == 2024 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - ], - ), - ), - const SizedBox(height: 20), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Filter by Language:', - style: TextStyle(fontWeight: FontWeight.w400), - ), - const SizedBox(width: 8), - DropdownWidget( - items: languages, - hintText: 'Language', - onChanged: (newValue) { - setState(() { - selectedLanguage = newValue; - resetProjectsByLanguage(); - }); - }, - ), - ], - ), - const SizedBox(height: 20), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Filter by Name:', - style: TextStyle(fontWeight: FontWeight.w400), - ), - const SizedBox(width: 8), - Expanded( - child: DropdownButton( - value: selectedOrg, - hint: const Text('Organization'), - isExpanded: true, - items: orgsList - .map((org) => DropdownMenuItem( - child: Text(org), - value: org, - )) - .toList(), - onChanged: (newValue) { - setState(() { - selectedOrg = newValue!; - resetProjectsByLanguage(); - if (selectedOrg != 'All') { - filterProjectsBySearchText(selectedOrg); - } - }); - }, - ), - ), - ], - ), - const SizedBox(height: 20), - Expanded( - child: ListView.builder( - itemCount: projectList.length, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: GssocProjectWidget( - index: index + 1, - modal: projectList[index], - height: height * 0.2, - width: width, - ), - ); - }, - ), - ), - ], - ), - ); - } else { - return const Center(child: Text("Some error occurred")); - } - }), + future: getProjectFunction, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.connectionState == ConnectionState.done) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildSearchBar(), + const SizedBox(height: 20), + _buildYearButtons(), + const SizedBox(height: 20), + _buildMultiSelectField( + items: allLanguages, + selectedValues: selectedLanguages, + title: "Select Languages", + buttonText: "Filter by Language", + onConfirm: (results) { + setState(() { + selectedLanguages = results.isNotEmpty ? results : ['All']; + filterProjects(); + }); + }, + ), + const SizedBox(height: 20), + _buildMultiSelectField( + items: allOrganizations, + selectedValues: selectedOrganizations, + title: "Select Organizations", + buttonText: "Filter by Name", + onConfirm: (results) { + setState(() { + selectedOrganizations = results.isNotEmpty ? results : ['All']; + print("Selected Organizations: $selectedOrganizations"); + filterProjects(); + }); + }, + ), + const SizedBox(height: 20), + _buildProjectList(height, width), + ], + ), + ); + } else { + return const Center(child: Text("Some error occurred")); + } + }, + ), + ), + ); + } + + + Widget _buildSearchBar() { + return TextFormField( + decoration: InputDecoration( + filled: true, + hintText: 'Search', + suffixIcon: const Icon(Icons.search), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + contentPadding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 20.0), + ), + onFieldSubmitted: (value) { + setState(() { + projectList = _getProjectsByYear() + .where((project) => project.name.toLowerCase().contains(value.toLowerCase())) + .toList(); + }); + }, + onChanged: (value) { + if (value.isEmpty) { + setState(() { + projectList = _getProjectsByYear(); + }); + } + }, + ); + } + + + Widget _buildYearButtons() { + return SizedBox( + height: MediaQuery.sizeOf(context).height * 0.2, + child: GridView( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 1.5 / 0.6, + crossAxisCount: 2, + crossAxisSpacing: 15, + mainAxisSpacing: 15, + ), + children: [ + YearButton( + year: "2021", + isEnabled: selectedYear == 2021, + onTap: () { + setState(() { + selectedYear = 2021; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2021 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2022", + isEnabled: selectedYear == 2022, + onTap: () { + setState(() { + selectedYear = 2022; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2022 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2023", + isEnabled: selectedYear == 2023, + onTap: () { + setState(() { + selectedYear = 2023; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2023 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2024", + isEnabled: selectedYear == 2024, + onTap: () { + setState(() { + selectedYear = 2024; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2024 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + ], + ), + ); + } + + + Widget _buildMultiSelectField({ + required List items, + required List selectedValues, + required String title, + required String buttonText, + required void Function(List) onConfirm, + }) { + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + return MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: items.map((e) => MultiSelectItem(e, e)).toList(), + initialValue: selectedValues, + title: Text(title,style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: Text(buttonText), + onConfirm: onConfirm, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), + ), + ); + } + + + Widget _buildProjectList(double height, double width) { + return Expanded( + child: ListView.builder( + itemCount: projectList.length, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: GssocProjectWidget( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ), + ); + }, ), ); } diff --git a/lib/programs screen/google_season_of_docs_screen.dart b/lib/programs screen/google_season_of_docs_screen.dart index 4039687..8852191 100644 --- a/lib/programs screen/google_season_of_docs_screen.dart +++ b/lib/programs screen/google_season_of_docs_screen.dart @@ -1,8 +1,7 @@ import 'dart:convert'; - - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:multi_select_flutter/multi_select_flutter.dart'; import 'package:opso/modals/book_mark_model.dart'; import 'package:opso/modals/gsod/gsod_modal_new.dart'; import 'package:opso/modals/gsod/gsod_modal_old.dart'; @@ -19,8 +18,8 @@ class GoogleSeasonOfDocsScreen extends StatefulWidget { class _GoogleSeasonOfDocsScreenState extends State { - String selectedOrganization = 'All'; - String selectedProposal = 'All'; + List selectedOrganizations = ['All']; + List selectedProposals = ['All']; String currentProgram = "Google Season of Docs"; bool isBookmarked = true; String currentPage = "/google_season_of_docs"; @@ -29,7 +28,6 @@ class _GoogleSeasonOfDocsScreenState extends State { List gsod2021 = []; List gsod2020 = []; List gsod2019 = []; - bool flag = true; int selectedYear = 2023; @@ -44,15 +42,13 @@ class _GoogleSeasonOfDocsScreenState extends State { for (var data in jsonList) { gsod2023.add(GsodModalNew.fromMap(data)); } - print(gsod2023.length); projectList = List.from(gsod2023); + + response = await rootBundle.loadString('assets/projects/gsod/gsod2022.json'); jsonList = await json.decode(response); - - for (var data in jsonList) { - print(data["organization_name"]); gsod2022.add(GsodModalNew.fromMap(data)); } @@ -60,17 +56,19 @@ class _GoogleSeasonOfDocsScreenState extends State { response = await rootBundle.loadString('assets/projects/gsod/gsod2021.json'); jsonList = await json.decode(response); - - for (var data in jsonList) { gsod2021.add(GsodModalNew.fromMap(data)); } + + response = await rootBundle.loadString('assets/projects/gsod/gsod2020.json'); jsonList = await json.decode(response); for (var data in jsonList) { gsod2020.add(GsodModalOld.fromMap(data)); } + + response = await rootBundle.loadString('assets/projects/gsod/gsod2019.json'); jsonList = await json.decode(response); @@ -117,30 +115,18 @@ class _GoogleSeasonOfDocsScreenState extends State { element.caseStudy .toLowerCase() .contains(searchText.toLowerCase()) || - element.docsPage - .toLowerCase() - .contains(searchText.toLowerCase()), + element.docsPage.toLowerCase().contains(searchText.toLowerCase()), ) .toList(); } else { projectList = projectList .where( (element) => - element.organization - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.technicalWriter - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.mentor - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.project - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.originalProjectProposal - .toLowerCase() - .contains(searchText.toLowerCase()) || + element.organization.toLowerCase().contains(searchText.toLowerCase()) || + element.technicalWriter.toLowerCase().contains(searchText.toLowerCase()) || + element.mentor.toLowerCase().contains(searchText.toLowerCase()) || + element.project.toLowerCase().contains(searchText.toLowerCase()) || + element.originalProjectProposal.toLowerCase().contains(searchText.toLowerCase()) || element.report.toLowerCase().contains(searchText.toLowerCase()), ) .toList(); @@ -154,11 +140,10 @@ class _GoogleSeasonOfDocsScreenState extends State { Future _refresh() async { - // Fetch data for the next year based on the currently selected year setState(() { initializeProjectLists(); - selectedOrganization = 'All'; - selectedProposal = 'All'; + selectedOrganizations = ['All']; + selectedProposals = ['All']; selectedYear = 2023; if (selectedYear > 2023) selectedYear = 2019; // Reset to the beginning if it exceeds 2023 }); @@ -186,53 +171,69 @@ class _GoogleSeasonOfDocsScreenState extends State { projectList = gsod2023; break; } - if (selectedOrganization != 'All') { - filterProjectsByTag(selectedOrganization); - } - setState(() { - _resetValueIfNotValid(); - }); + filterProjects(); } - void filterProjectsByTag(String tag) { - projectList = projectList - .where((element) => element.tags.contains(tag)) - .toList(); + void filterProjects() { + var filteredProjects = List.from(projectList); + print("!@# $selectedOrganizations"); + + + if (!selectedOrganizations.contains('All')) { + filteredProjects = filteredProjects.where((project) { + return selectedOrganizations.contains(project.organizationName); + }).toList(); + }else{ + filteredProjects = gsod2023; + } + + + if (!selectedProposals.contains('All')) { + filteredProjects = filteredProjects.where((project) { + return selectedProposals.contains(project.acceptedProjectProposal); + }).toList(); + } + + + + + + setState(() { + projectList = filteredProjects; _resetValueIfNotValid(); }); } void _resetValueIfNotValid() { - // Reset selectedOrganization if it is not valid - var validOrganizations = projectList - .map((project) => project.organizationName) - .toSet(); // Use a Set to ensure uniqueness - if (!validOrganizations.contains(selectedOrganization)) { - selectedOrganization = 'All'; + // Reset selectedOrganizations if they are not valid + var validOrganizations = projectList.map((project) => project.organizationName).toSet(); + selectedOrganizations = selectedOrganizations.where((org) => validOrganizations.contains(org) || org == 'All').toList(); + if (selectedOrganizations.isEmpty) { + selectedOrganizations = ['All']; } - // Reset selectedProposal if it is not valid - var validProposals = projectList - .map((project) => project.acceptedProjectProposal) - .toSet(); // Use a Set to ensure uniqueness - if (!validProposals.contains(selectedProposal)) { - selectedProposal = 'All'; + // Reset selectedProposals if they are not valid + var validProposals = projectList.map((project) => project.acceptedProjectProposal).toSet(); + selectedProposals = selectedProposals.where((proposal) => validProposals.contains(proposal) || proposal == 'All').toList(); + if (selectedProposals.isEmpty) { + selectedProposals = ['All']; } } @override Widget build(BuildContext context) { + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; var height = MediaQuery.sizeOf(context).height; var width = MediaQuery.sizeOf(context).width; return RefreshIndicator( onRefresh: _refresh, child: Scaffold( - appBar: AppBar(title: const Text('OpSo'), actions: [ + appBar: AppBar(title: const Text('GSoD'), actions: [ IconButton( icon: (isBookmarked) ? const Icon(Icons.bookmark_add_rounded) @@ -243,16 +244,13 @@ class _GoogleSeasonOfDocsScreenState extends State { }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - isBookmarked ? 'Bookmark added' : 'Bookmark removed'), + content: Text(isBookmarked ? 'Bookmark added' : 'Bookmark removed'), duration: const Duration(seconds: 2), // Adjust the duration as needed ), ); if (isBookmarked) { - print("Adding"); HandleBookmark.addBookmark(currentProgram, currentPage); } else { - print("Deleting"); HandleBookmark.deleteBookmark(currentProgram); } }, @@ -264,227 +262,218 @@ class _GoogleSeasonOfDocsScreenState extends State { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.connectionState == ConnectionState.done) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 46, vertical: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - decoration: InputDecoration( - filled: true, - // fillColor: const Color(0xFFEEEEEE), - hintText: 'Search', - suffixIcon: const Icon(Icons.search), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 46, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, + children: [ + TextFormField( + decoration: InputDecoration( + filled: true, + hintText: 'Search', + suffixIcon: const Icon(Icons.search), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), - ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), + contentPadding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 20.0), ), - contentPadding: const EdgeInsets.symmetric( - vertical: 12.0, horizontal: 20.0), - ), - onFieldSubmitted: (value) { - print("value is $value"); - search(value.trim()); - }, - onChanged: (value) { - if (value.isEmpty) { - search(value); - } - }, - ), - const SizedBox(height: 20), - Container( - constraints: BoxConstraints( - maxHeight: height * 0.3, - ), - width: width, - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: 1.5 / 0.6, - crossAxisSpacing: 15, - mainAxisSpacing: 15, - ), - children: [ - YearButton( - year: "2019", - isEnabled: selectedYear == 2019 ? true : false, - onTap: () { - setState(() { - projectList = gsod2019; - selectedYear = 2019; - _resetValueIfNotValid(); - }); - }, - backgroundColor: selectedYear == 2019 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2020", - isEnabled: selectedYear == 2020 ? true : false, - onTap: () { - setState(() { - projectList = gsod2020; - selectedYear = 2020; - _resetValueIfNotValid(); - }); - }, - backgroundColor: selectedYear == 2020 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2021", - isEnabled: selectedYear == 2021 ? true : false, - onTap: () { - setState(() { - projectList = gsod2021; - selectedYear = 2021; - _resetValueIfNotValid(); - }); - }, - backgroundColor: selectedYear == 2021 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2022", - isEnabled: selectedYear == 2022 ? true : false, - onTap: () { - setState(() { - projectList = gsod2022; - selectedYear = 2022; - _resetValueIfNotValid(); - }); - }, - backgroundColor: selectedYear == 2022 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - YearButton( - year: "2023", - isEnabled: selectedYear == 2023 ? true : false, - onTap: () { - setState(() { - projectList = gsod2023; - selectedYear = 2023; - _resetValueIfNotValid(); - }); - }, - backgroundColor: selectedYear == 2023 - ? Colors.white - : const Color.fromRGBO(249, 171, 0, 1), - ), - ], + onFieldSubmitted: (value) { + search(value.trim()); + }, + onChanged: (value) { + if (value.isEmpty) { + search(value); + } + }, ), - ), - const SizedBox( - height: 20, - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Filter by Organization:', - style: TextStyle(fontWeight: FontWeight.w400), + const SizedBox(height: 20), + Container( + constraints: BoxConstraints( + maxHeight: height * 0.3, ), - const SizedBox(width: 8), - Expanded( - child: DropdownButton( - value: selectedOrganization, - hint: const Text('Organization'), - isExpanded: true, - items: [ - DropdownMenuItem( - value: 'All', // Unique value for "All" - child: const Text('All'), - ), - ...projectList - .map((project) => project.organizationName) - .toSet() // Use a Set to ensure uniqueness - .map((organization) { - return DropdownMenuItem( - value: organization, - child: Text(organization), - ); - }).toList(), - ], - onChanged: (newValue) { - setState(() { - selectedOrganization = newValue!; - search(newValue); - }); - }, + width: width, + child: GridView( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 1.5 / 0.6, + crossAxisSpacing: 15, + mainAxisSpacing: 15, ), + children: [ + YearButton( + year: "2019", + isEnabled: selectedYear == 2019, + onTap: () { + setState(() { + projectList = gsod2019; + selectedYear = 2019; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2019 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2020", + isEnabled: selectedYear == 2020, + onTap: () { + setState(() { + projectList = gsod2020; + selectedYear = 2020; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2020 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2021", + isEnabled: selectedYear == 2021, + onTap: () { + setState(() { + projectList = gsod2021; + selectedYear = 2021; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2021 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2022", + isEnabled: selectedYear == 2022, + onTap: () { + setState(() { + projectList = gsod2022; + selectedYear = 2022; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2022 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + YearButton( + year: "2023", + isEnabled: selectedYear == 2023, + onTap: () { + setState(() { + projectList = gsod2023; + selectedYear = 2023; + _resetValueIfNotValid(); + }); + }, + backgroundColor: selectedYear == 2023 + ? Colors.white + : const Color.fromRGBO(249, 171, 0, 1), + ), + ], ), - ], - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Filter by Accepted Proposal:', - style: TextStyle(fontWeight: FontWeight.w400), + ), + const SizedBox(height: 20), + MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: [ + MultiSelectItem('All', 'All'), + ...projectList + .map((project) => project.organizationName) + .toSet() + .map((organization) { + return MultiSelectItem(organization, organization); + }).toList(), + ], + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), ), - const SizedBox(width: 8), - Expanded( - child: DropdownButton( - value: selectedProposal, - hint: const Text('Proposal'), - isExpanded: true, - items: [ - DropdownMenuItem( - value: 'All', // Unique value for "All" - child: const Text('All'), - ), - ...projectList - .map((project) => - project.acceptedProjectProposal) - .toSet() // Use a Set to ensure uniqueness - .toList() - .map((proposal) { - return DropdownMenuItem( - value: proposal, - child: Text(proposal), - ); - }).toList(), - ], - onChanged: (newValue) { - setState(() { - selectedProposal = newValue!; - search(newValue); - }); - }, - ), + title: Text("Select Organizations", style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: const Text("Filter by Organization"), + onConfirm: (results) { + setState(() { + print("results are $results"); + if (results.contains('All')) { + selectedOrganizations = ['All']; + } else { + selectedOrganizations = results.isNotEmpty ? results : ['All']; + } + + + print("this is selected organization $selectedOrganizations"); + filterProjects(); + }); + }, + initialValue: selectedOrganizations, + ), + + + const SizedBox(height: 20), + MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: [ + MultiSelectItem('All', 'All'), + ...projectList + .map((project) => project.acceptedProjectProposal) + .toSet() + .map((proposal) { + return MultiSelectItem(proposal, proposal); + }).toList(), + ], + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), ), - ], - ), - const SizedBox(height: 20), - Expanded( - child: ListView.builder( + title: Text("Select Proposals", style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: const Text("Filter by Accepted Proposal"), + onConfirm: (results) { + setState(() { + print("results are $results"); + if (results.contains('All')) { + selectedProposals = ['All']; + } else { + selectedProposals = results.isNotEmpty ? results : ['All']; + } + + + print("this is selected proposal $selectedProposals"); + filterProjects(); + }); + }, + initialValue: selectedProposals, + ), + + + const SizedBox(height: 20), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), itemCount: projectList.length, itemBuilder: (BuildContext context, int index) { return Padding( @@ -505,8 +494,8 @@ class _GoogleSeasonOfDocsScreenState extends State { ); }, ), - ), - ], + ], + ), ), ); } else { diff --git a/lib/programs screen/google_summer_of_code_screen.dart b/lib/programs screen/google_summer_of_code_screen.dart index 1329e69..4b3f1b2 100644 --- a/lib/programs screen/google_summer_of_code_screen.dart +++ b/lib/programs screen/google_summer_of_code_screen.dart @@ -2,6 +2,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:opso/widgets/gsoc/GsocProjectWidget.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:multi_select_flutter/multi_select_flutter.dart'; import '../modals/GSoC/Gsoc.dart'; import '../services/ApiService.dart'; import '../widgets/SearchandFilterWidget.dart'; @@ -15,7 +16,7 @@ class GoogleSummerOfCodeScreen extends StatefulWidget { class _GoogleSummerOfCodeScreenState extends State { - String selectedOrg = 'All'; + String selectedOrg = 'All'; // Ensure this is defined List gsoc2024 = []; List gsoc2023 = []; List gsoc2022 = []; @@ -39,6 +40,9 @@ class _GoogleSummerOfCodeScreenState extends State { 'Dart' ]; List orgList = []; + List selectedLanguages = ['All']; + List allOrganizations = []; + List selectedOrganizations = ['All']; late Future _dataFetchFuture; @@ -64,6 +68,7 @@ class _GoogleSummerOfCodeScreenState extends State { gsoc2023 = orgData2023.organizations ?? []; gsoc2024 = orgData2024.organizations ?? []; orgList = gsoc2024; // Default to the latest year + allOrganizations = ['All', ...orgList.map((org) => org.name!).toSet()]; }); } catch (e) { print('Error: $e'); @@ -71,27 +76,31 @@ class _GoogleSummerOfCodeScreenState extends State { } - void searchTag(String searchTag) { - setState(() { - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch - orgList = _getOrganizationsByYear(selectedYear) - .where((element) => element.technologies?.contains(searchTag) == true || element.topics?.contains(searchTag) == true) - .toList(); - }); - } + void filterProjects() { + orgList = _getOrganizationsByYear(selectedYear); - void search(String searchText) { - setState(() { - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch - if (searchText.isEmpty) { - orgList = _getOrganizationsByYear(selectedYear); - } else { - orgList = _getOrganizationsByYear(selectedYear) - .where((element) => element.name?.toLowerCase().contains(searchText.toLowerCase()) == true) - .toList(); - } - }); + if (!selectedLanguages.contains('All')) { + orgList = orgList.where((project) => project.technologies?.any(selectedLanguages.contains) == true).toList(); + } + + + if (!selectedOrganizations.contains('All')) { + orgList = orgList.where((project) => selectedOrganizations.contains(project.name)).toList(); + } + + + // Update organization filter based on selected languages + allOrganizations = [ + 'All', + ..._getOrganizationsByYear(selectedYear) + .where((org) => selectedLanguages.contains('All') || org.technologies?.any(selectedLanguages.contains) == true) + .map((org) => org.name!) + .toSet() + ]; + + + setState(() {}); } @@ -112,21 +121,25 @@ class _GoogleSummerOfCodeScreenState extends State { Future _refresh() async { + await getProjectData(); setState(() { - _dataFetchFuture = getProjectData(); selectedYear = 2024; - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch + selectedLanguages = ['All']; + selectedOrganizations = ['All']; + filterProjects(); }); } - void resetProjectsByOrganization() { + // Add this method to the _GoogleSummerOfCodeScreenState class + void search(String searchText) { setState(() { - if (selectedOrg == 'All') { + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch + if (searchText.isEmpty) { orgList = _getOrganizationsByYear(selectedYear); } else { orgList = _getOrganizationsByYear(selectedYear) - .where((element) => element.name == selectedOrg) + .where((element) => element.name?.toLowerCase().contains(searchText.toLowerCase()) == true) .toList(); } }); @@ -219,8 +232,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2021; - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch - orgList = gsoc2021; + selectedLanguages = ['All']; + selectedOrganizations = ['All']; + filterProjects(); }); }, backgroundColor: selectedYear == 2021 @@ -233,8 +247,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2022; - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch - orgList = gsoc2022; + selectedLanguages = ['All']; + selectedOrganizations = ['All']; + filterProjects(); }); }, backgroundColor: selectedYear == 2022 @@ -247,8 +262,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2023; - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch - orgList = gsoc2023; + selectedLanguages = ['All']; + selectedOrganizations = ['All']; + filterProjects(); }); }, backgroundColor: selectedYear == 2023 @@ -261,8 +277,9 @@ class _GoogleSummerOfCodeScreenState extends State { onTap: () { setState(() { selectedYear = 2024; - selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch - orgList = gsoc2024; + selectedLanguages = ['All']; + selectedOrganizations = ['All']; + filterProjects(); }); }, backgroundColor: selectedYear == 2024 @@ -272,64 +289,31 @@ class _GoogleSummerOfCodeScreenState extends State { ], ), ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Text( - 'Filter by Language:', - style: TextStyle(fontWeight: FontWeight.w400), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - DropdownWidget( - items: languages, - hintText: 'Language', - onChanged: (newValue) { - searchTag(newValue); - }, - ), - ], - ), - ) - ], + const SizedBox(height: 20), + _buildMultiSelectField( + items: languages, + selectedValues: selectedLanguages, + title: "Select Languages", + buttonText: "Filter by Language", + onConfirm: (results) { + setState(() { + selectedLanguages = results.isNotEmpty ? results : ['All']; + filterProjects(); + }); + }, ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Text( - 'Filter by Organization:', - style: TextStyle(fontWeight: FontWeight.w400), - ), - const SizedBox(width: 8), - Expanded( - child: DropdownButton( - value: selectedOrg, - hint: const Text('Organization'), - isExpanded: true, - items: [ - 'All', - if (orgList.isNotEmpty) - ...orgList.map((org) => org.name!).toList() - ].map((String value) { - return DropdownMenuItem( - value: value, - child: Text(value), - ); - }).toList(), - onChanged: (newValue) { - setState(() { - selectedOrg = newValue!; - resetProjectsByOrganization(); - }); - }, - ), - ), - ], + const SizedBox(height: 20), + _buildMultiSelectField( + items: allOrganizations, + selectedValues: selectedOrganizations, + title: "Select Organizations", + buttonText: "Filter by Organization", + onConfirm: (results) { + setState(() { + selectedOrganizations = results.isNotEmpty ? results : ['All']; + filterProjects(); + }); + }, ), const SizedBox(height: 20), Expanded( @@ -371,6 +355,29 @@ class _GoogleSummerOfCodeScreenState extends State { ), ); } + + + Widget _buildMultiSelectField({ + required List items, + required List selectedValues, + required String title, + required String buttonText, + required void Function(List) onConfirm, + }) { + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + return MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: items.map((e) => MultiSelectItem(e, e)).toList(), + initialValue: selectedValues, + title: Text(title, style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: Text(buttonText), + onConfirm: onConfirm, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), + ), + ); + } } diff --git a/pubspec.lock b/pubspec.lock index 4a2f4ef..a29db0e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -304,6 +304,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.0" + multi_select_flutter: + dependency: "direct main" + description: + name: multi_select_flutter + sha256: "503857b415d390d29159df8a9d92d83c6aac17aaf1c307fb7bcfc77d097d20ed" + url: "https://pub.dev" + source: hosted + version: "4.1.3" path: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 18512c6..c11c04b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,7 @@ dependencies: adaptive_theme: ^3.6.0 timeline_tile: ^2.0.0 flutter_svg: ^2.0.10+1 - + multi_select_flutter: ^4.1.3 dev_dependencies: awesome_notifications: ^0.9.3+1 flutter_launcher_icons: ^0.13.1 From f3f8ebb11e0d5ec337d2ee2c631ec7594ab0f2c9 Mon Sep 17 00:00:00 2001 From: Saumya-28 Date: Thu, 30 May 2024 13:58:34 +0530 Subject: [PATCH 3/4] Updated Multi-Select Filter Feature --- lib/programs screen/girl_script.dart | 693 +++++++++--------- .../google_season_of_docs_screen.dart | 409 +++++++---- .../google_summer_of_code_screen.dart | 558 +++++++------- pubspec.lock | 32 +- pubspec.yaml | 2 +- 5 files changed, 920 insertions(+), 774 deletions(-) diff --git a/lib/programs screen/girl_script.dart b/lib/programs screen/girl_script.dart index 06f5340..c9d0409 100644 --- a/lib/programs screen/girl_script.dart +++ b/lib/programs screen/girl_script.dart @@ -3,68 +3,111 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:opso/modals/book_mark_model.dart'; import 'package:opso/modals/gssoc_project_modal.dart'; - -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:opso/widgets/gssoc_project_widget.dart'; import 'package:opso/widgets/year_button.dart'; - import '../widgets/SearchandFilterWidget.dart'; + +import 'package:multi_select_flutter/multi_select_flutter.dart'; + + class GSSOCScreen extends StatefulWidget { const GSSOCScreen({super.key}); + @override State createState() => _GSSOCScreenState(); } + class _GSSOCScreenState extends State { - String currectPage = "/girl_script_summer_of_code"; + String currentPage = "/girl_script_summer_of_code"; String currentProject = "Girl Script Summer of Code"; List gssoc2024 = []; List gssoc2023 = []; - bool isBookmarked = true; List gssoc2022 = []; List gssoc2021 = []; + List allOrganizations = []; + List allLanguages = []; + List selectedOrganizations = ['All']; + List selectedLanguages = ['All']; int selectedYear = 2024; - String selectedOrg = "All"; + bool isBookmarked = true; List projectList = []; Future? getProjectFunction; + Future initializeProjectLists() async { - String response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2024.json'); - var jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2024.add(GssocProjectModal.getDataFromJson(data)); - } - projectList = List.from(gssoc2024); - response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2023.json'); - jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2023.add(GssocProjectModal.getDataFromJson(data)); - } - response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2022.json'); - jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2022.add(GssocProjectModal.getDataFromJson(data)); - } - response = - await rootBundle.loadString('assets/projects/gssoc/gssoc2021.json'); - jsonList = await json.decode(response); - for (var data in jsonList) { - gssoc2021.add(GssocProjectModal.getDataFromJson(data)); - } + await _loadProjects('assets/projects/gssoc/gssoc2024.json', gssoc2024); + await _loadProjects('assets/projects/gssoc/gssoc2023.json', gssoc2023); + await _loadProjects('assets/projects/gssoc/gssoc2022.json', gssoc2022); + await _loadProjects('assets/projects/gssoc/gssoc2021.json', gssoc2021); + + + // Populate all unique organizations and languages + allOrganizations = _extractUniqueValues((project) => project.hostedBy); + allLanguages = languages; + projectList = List.from(gssoc2024); // Default year + } + + + List languages = [ + 'All', + 'Js', + 'Python', + 'React', + 'Angular', + 'Bootstrap', + 'Firebase', + 'Node', + 'MongoDb', + 'Express', + 'Next', + 'CSS', + 'HTML', + 'JavaScript', + 'Flutter', + 'Dart' + ]; + + + Future _loadProjects(String path, List list) async { + String response = await rootBundle.loadString(path); + var jsonList = json.decode(response) as List; + list.addAll(jsonList.map((data) => GssocProjectModal.getDataFromJson(data)).toList()); + } + + + List _extractUniqueValues(String Function(GssocProjectModal) extractor) { + return { + 'All', + ...gssoc2024.map(extractor), + ...gssoc2023.map(extractor), + ...gssoc2022.map(extractor), + ...gssoc2021.map(extractor), + }.toList(); + } + + + List _extractUniqueLanguages(List Function(GssocProjectModal) extractor) { + final allLanguages = [ + for (var project in gssoc2024) ...extractor(project), + for (var project in gssoc2023) ...extractor(project), + for (var project in gssoc2022) ...extractor(project), + for (var project in gssoc2021) ...extractor(project), + ]; + return ['All', ...allLanguages.toSet()]; } + @override void initState() { - getProjectFunction = initializeProjectLists(); super.initState(); + getProjectFunction = initializeProjectLists(); _checkBookmarkStatus(); } + Future _checkBookmarkStatus() async { bool bookmarkStatus = await HandleBookmark.isBookmarked(currentProject); setState(() { @@ -72,326 +115,302 @@ class _GSSOCScreenState extends State { }); } - void searchTag(String searchTag) { - projectList = projectList - .where((element) => element.techstack.contains(searchTag)) - .toList(); + + void filterProjects() { + // Filter projects by year first (if applicable) + projectList = _getProjectsByYear(); + + + // Filter projects by selected languages + if (!selectedLanguages.contains('All')) { + projectList = projectList.where((project) => project.techstack.any(selectedLanguages.contains)).toList(); + } + + + // Update the list of organizations based on the filtered projects by language + _updateOrganizationList(); + + + // Filter projects by selected organizations + if (!selectedOrganizations.contains('All')) { + projectList = projectList.where((project) => selectedOrganizations.contains(project.hostedBy)).toList(); + } + + + // Ensure state is updated to reflect changes setState(() {}); } + void _updateOrganizationList() { + allOrganizations = _extractUniqueValues((project) => project.hostedBy) + .where((organization) => projectList.any((project) => project.hostedBy == organization)) + .toList(); + allOrganizations.insert(0, 'All'); + } + - void search(String searchText) { - if (searchText.isEmpty) { - switch (selectedYear) { - case 2021: - projectList = gssoc2021; - break; - case 2022: - projectList = gssoc2022; - break; - case 2023: - projectList = gssoc2023; - break; - case 2024: - projectList = gssoc2024; - break; - } - setState(() {}); - return; + List _getProjectsByYear() { + switch (selectedYear) { + case 2021: + return gssoc2021; + case 2022: + return gssoc2022; + case 2023: + return gssoc2023; + case 2024: + return gssoc2024; + default: + return []; } - projectList = projectList - .where( - (element) => - element.name.toLowerCase().contains(searchText.toLowerCase()) || - element.techstack.contains(searchText) || - element.hostedBy.toLowerCase().contains(searchText.toLowerCase()), - ) - .toList(); - setState(() {}); } + Future _refresh() async { + await initializeProjectLists(); setState(() { - initializeProjectLists(); selectedYear = 2024; + selectedOrganizations = ['All']; + selectedLanguages = ['All']; }); } + @override Widget build(BuildContext context) { - ScreenUtil.init(context); - // var height = MediaQuery.sizeOf(context).height; - // var width = MediaQuery.sizeOf(context).width; - List languages = [ - 'All', - 'Js', - 'Python', - 'React', - 'Angular', - 'Bootstrap', - 'Firebase', - 'Node', - 'MongoDb', - 'Express', - 'Next', - 'CSS', - 'HTML', - 'JavaScript', - 'Flutter', - 'Dart' - ]; + var height = MediaQuery.of(context).size.height; + var width = MediaQuery.of(context).size.width; + + return RefreshIndicator( - onRefresh: _refresh, - child: Scaffold( - appBar: AppBar(title: const Text('GSSoC'), actions: [ - IconButton( - icon: (isBookmarked) - ? const Icon(Icons.bookmark_add_rounded) - : const Icon(Icons.bookmark_add_outlined), - onPressed: () { - setState(() { - isBookmarked = !isBookmarked; - }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - isBookmarked ? 'Bookmark added' : 'Bookmark removed'), - duration: const Duration( - seconds: 2), // Adjust the duration as needed + onRefresh: _refresh, + child: Scaffold( + appBar: AppBar(title: const Text('GSSoC'), actions: [ + IconButton( + icon: (isBookmarked) + ? const Icon(Icons.bookmark_add_rounded) + : const Icon(Icons.bookmark_add_outlined), + onPressed: () { + setState(() { + isBookmarked = !isBookmarked; + }); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(isBookmarked ? 'Bookmark added' : 'Bookmark removed'), + duration: const Duration(seconds: 2), + ), + ); + if (isBookmarked) { + HandleBookmark.addBookmark(currentProject, currentPage); + } else { + HandleBookmark.deleteBookmark(currentProject); + } + }, + ) + ]), + body: FutureBuilder( + future: getProjectFunction, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.connectionState == ConnectionState.done) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 48, vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _buildSearchBar(), + const SizedBox(height: 20), + _buildYearButtons(), + const SizedBox(height: 20), + _buildMultiSelectField( + items: allLanguages, + selectedValues: selectedLanguages, + title: "Select Languages", + buttonText: "Filter by Language", + onConfirm: (results) { + setState(() { + selectedLanguages = results.isNotEmpty ? results : ['All']; + filterProjects(); + }); + }, + ), + const SizedBox(height: 20), + _buildMultiSelectField( + items: allOrganizations, + selectedValues: selectedOrganizations, + title: "Select Organizations", + buttonText: "Filter by Name", + onConfirm: (results) { + setState(() { + selectedOrganizations = results.isNotEmpty ? results : ['All']; + print("Selected Organizations: $selectedOrganizations"); + filterProjects(); + }); + }, + ), + const SizedBox(height: 20), + _buildProjectList(height, width), + ], ), - ); - if (isBookmarked) { - print("Adding"); - HandleBookmark.addBookmark(currentProject, currectPage); - } else { - print("Deleting"); - HandleBookmark.deleteBookmark(currentProject); - } - }, - ) - ]), - body: FutureBuilder( - future: getProjectFunction, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.connectionState == ConnectionState.done) { - return Padding( - padding: EdgeInsets.symmetric( - horizontal: ScreenUtil().setWidth(46), - vertical: ScreenUtil().setHeight(16)), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - decoration: InputDecoration( - filled: true, - // fillColor: const Color(0xFFEEEEEE), - hintText: 'Search', - suffixIcon: const Icon(Icons.search), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), - ), - ), - contentPadding: EdgeInsets.symmetric( - vertical: ScreenUtil().setHeight(12), - horizontal: ScreenUtil().setWidth(20)), - ), - onFieldSubmitted: (value) { - print("value is $value"); - search(value.trim()); - }, - onChanged: (value) { - if (value.isEmpty) { - search(value); - } - }, - ), - const SizedBox(height: 20), - SizedBox( - height: ScreenUtil().screenHeight * 0.2, - width: ScreenUtil().screenWidth, - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: 1.5 / 0.6, - crossAxisSpacing: 15, - mainAxisSpacing: 15, - ), - children: [ - YearButton( - year: "2021", - isEnabled: selectedYear == 2021 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2021; - selectedYear = 2021; - }); - }, - backgroundColor: selectedYear == 2021 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2022", - isEnabled: selectedYear == 2022 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2022; - selectedYear = 2022; - }); - }, - backgroundColor: selectedYear == 2022 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2023", - isEnabled: selectedYear == 2023 ? true : false, - onTap: () { - setState(() { - projectList = gssoc2023; - selectedYear = 2023; - }); - }, - backgroundColor: selectedYear == 2023 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - isEnabled: selectedYear == 2024 ? true : false, - year: "2024", - onTap: () { - setState(() { - projectList = gssoc2024; - selectedYear = 2024; - }); - }, - backgroundColor: selectedYear == 2024 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - ], - ), - ), - // const SizedBox(height: 20), - // SizedBox( - // height: 50, - // child: ElevatedButton( - // onPressed: () {}, - // style: ElevatedButton.styleFrom( - // shape: const RoundedRectangleBorder( - // borderRadius: BorderRadius.zero, - // ), - // backgroundColor: const Color.fromARGB( - // 255, 253, 214, 115), // Set button color - // padding: const EdgeInsets.symmetric( - // vertical: 10.0, horizontal: 20.0), - // ), - // child: const Text( - // 'View Projects', - // style: TextStyle(color: Colors.white, fontSize: 18), - // ), - // ), - // ), - SizedBox( - height: ScreenUtil().setHeight(20), - ), - - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - 'Filter by Language:', - style: TextStyle( - fontWeight: FontWeight.w400, - fontSize: ScreenUtil().setSp(14)), - ), - Padding( - padding: - EdgeInsets.all(ScreenUtil().setHeight(8)), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - DropdownWidget( - items: languages, - hintText: 'Language', - onChanged: (newValue) { - setState(() { - switch (selectedYear) { - case 2021: - projectList = gssoc2021; - break; - case 2022: - projectList = gssoc2022; - break; - case 2023: - projectList = gssoc2023; - break; - case 2024: - projectList = gssoc2024; - break; - } - searchTag(newValue); - }); - // Perform filtering based on selectedLanguage - }, - ), - ], - ), - ) - ], - ), - - Expanded( - // width: width, - child: ListView.builder( - itemCount: projectList.length, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: - const EdgeInsets.symmetric(vertical: 10), - child: GssocProjectWidget( - index: index + 1, - modal: projectList[index], - height: ScreenUtil().screenHeight * 0.2, - width: ScreenUtil().screenWidth, - ), - ); - }, - ), - ), - ], - ), - ); - } else { - return const Center(child: Text("Some error occured")); - } - }), - )); + ), + ); + } else { + return const Center(child: Text("Some error occurred")); + } + }, + ), + ), + ); + } + + + Widget _buildSearchBar() { + return TextFormField( + decoration: InputDecoration( + filled: true, + hintText: 'Search', + suffixIcon: const Icon(Icons.search), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide(color: Color(0xFFEEEEEE)), + ), + contentPadding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 20.0), + ), + onFieldSubmitted: (value) { + setState(() { + projectList = _getProjectsByYear() + .where((project) => project.name.toLowerCase().contains(value.toLowerCase())) + .toList(); + }); + }, + onChanged: (value) { + if (value.isEmpty) { + setState(() { + projectList = _getProjectsByYear(); + }); + } + }, + ); } + + + Widget _buildYearButtons() { + return SizedBox( + height: MediaQuery.sizeOf(context).height * 0.2, + child: GridView( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + childAspectRatio: 1.5 / 0.6, + crossAxisCount: 2, + crossAxisSpacing: 15, + mainAxisSpacing: 15, + ), + children: [ + YearButton( + year: "2021", + isEnabled: selectedYear == 2021, + onTap: () { + setState(() { + selectedYear = 2021; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2021 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2022", + isEnabled: selectedYear == 2022, + onTap: () { + setState(() { + selectedYear = 2022; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2022 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2023", + isEnabled: selectedYear == 2023, + onTap: () { + setState(() { + selectedYear = 2023; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2023 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2024", + isEnabled: selectedYear == 2024, + onTap: () { + setState(() { + selectedYear = 2024; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2024 ? Colors.white : const Color.fromRGBO(255, 183, 77, 1), + ), + ], + ), + ); + } + + + Widget _buildMultiSelectField({ + required List items, + required List selectedValues, + required String title, + required String buttonText, + required void Function(List) onConfirm, + }) { + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + return MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: items.map((e) => MultiSelectItem(e, e)).toList(), + initialValue: selectedValues, + title: Text(title,style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: Text(buttonText), + onConfirm: onConfirm, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), + ), + ); + } + + + Widget _buildProjectList(double height, double width) { + return Container( + height: height, // Set a specific height here + child: ListView.builder( + itemCount: projectList.length, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: GssocProjectWidget( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ), + ); + }, + ), + ); + } + + } + diff --git a/lib/programs screen/google_season_of_docs_screen.dart b/lib/programs screen/google_season_of_docs_screen.dart index c8c7a5a..8852191 100644 --- a/lib/programs screen/google_season_of_docs_screen.dart +++ b/lib/programs screen/google_season_of_docs_screen.dart @@ -1,8 +1,7 @@ import 'dart:convert'; - -import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:multi_select_flutter/multi_select_flutter.dart'; import 'package:opso/modals/book_mark_model.dart'; import 'package:opso/modals/gsod/gsod_modal_new.dart'; import 'package:opso/modals/gsod/gsod_modal_old.dart'; @@ -10,13 +9,17 @@ import 'package:opso/widgets/gsod/gsod_project_widget_new.dart'; import 'package:opso/widgets/gsod/gsod_project_widget_old.dart'; import 'package:opso/widgets/year_button.dart'; + class GoogleSeasonOfDocsScreen extends StatefulWidget { @override State createState() => _GoogleSeasonOfDocsScreenState(); } + class _GoogleSeasonOfDocsScreenState extends State { + List selectedOrganizations = ['All']; + List selectedProposals = ['All']; String currentProgram = "Google Season of Docs"; bool isBookmarked = true; String currentPage = "/google_season_of_docs"; @@ -25,51 +28,56 @@ class _GoogleSeasonOfDocsScreenState extends State { List gsod2021 = []; List gsod2020 = []; List gsod2019 = []; - bool flag = true; int selectedYear = 2023; + List projectList = []; Future? getProjectFunction; + Future initializeProjectLists() async { String response = - await rootBundle.loadString('assets/projects/gsod/gsod2023.json'); + await rootBundle.loadString('assets/projects/gsod/gsod2023.json'); var jsonList = await json.decode(response); for (var data in jsonList) { gsod2023.add(GsodModalNew.fromMap(data)); } - print(gsod2023.length); projectList = List.from(gsod2023); + + response = - await rootBundle.loadString('assets/projects/gsod/gsod2022.json'); + await rootBundle.loadString('assets/projects/gsod/gsod2022.json'); jsonList = await json.decode(response); - for (var data in jsonList) { - print(data["organization_name"]); gsod2022.add(GsodModalNew.fromMap(data)); } + response = - await rootBundle.loadString('assets/projects/gsod/gsod2021.json'); + await rootBundle.loadString('assets/projects/gsod/gsod2021.json'); jsonList = await json.decode(response); - for (var data in jsonList) { gsod2021.add(GsodModalNew.fromMap(data)); } + + response = - await rootBundle.loadString('assets/projects/gsod/gsod2020.json'); + await rootBundle.loadString('assets/projects/gsod/gsod2020.json'); jsonList = await json.decode(response); for (var data in jsonList) { gsod2020.add(GsodModalOld.fromMap(data)); } + + response = - await rootBundle.loadString('assets/projects/gsod/gsod2019.json'); + await rootBundle.loadString('assets/projects/gsod/gsod2019.json'); jsonList = await json.decode(response); for (var data in jsonList) { gsod2019.add(GsodModalOld.fromMap(data)); } } + @override void initState() { getProjectFunction = initializeProjectLists(); @@ -77,6 +85,7 @@ class _GoogleSeasonOfDocsScreenState extends State { super.initState(); } + Future _checkBookmarkStatus() async { bool bookmarkStatus = await HandleBookmark.isBookmarked(currentProgram); setState(() { @@ -84,95 +93,147 @@ class _GoogleSeasonOfDocsScreenState extends State { }); } + void search(String searchText) { if (searchText.isEmpty) { - switch (selectedYear) { - case 2021: - projectList = gsod2021; - break; - case 2022: - projectList = gsod2022; - break; - case 2023: - projectList = gsod2023; - break; - case 20202: - projectList = gsod2020; - break; - case 2019: - projectList = gsod2019; - break; - } - setState(() {}); + resetProjectsByLanguage(); return; } if (selectedYear > 2020) { projectList = projectList .where( (element) => - element.organizationName - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.budget - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.acceptedProjectProposal - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.caseStudy - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.docsPage - .toLowerCase() - .contains(searchText.toLowerCase()), - ) + element.organizationName + .toLowerCase() + .contains(searchText.toLowerCase()) || + element.budget + .toLowerCase() + .contains(searchText.toLowerCase()) || + element.acceptedProjectProposal + .toLowerCase() + .contains(searchText.toLowerCase()) || + element.caseStudy + .toLowerCase() + .contains(searchText.toLowerCase()) || + element.docsPage.toLowerCase().contains(searchText.toLowerCase()), + ) .toList(); } else { projectList = projectList .where( (element) => - element.organization - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.technicalWriter - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.mentor - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.project - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.originalProjectProposal - .toLowerCase() - .contains(searchText.toLowerCase()) || - element.report.toLowerCase().contains(searchText.toLowerCase()), - ) + element.organization.toLowerCase().contains(searchText.toLowerCase()) || + element.technicalWriter.toLowerCase().contains(searchText.toLowerCase()) || + element.mentor.toLowerCase().contains(searchText.toLowerCase()) || + element.project.toLowerCase().contains(searchText.toLowerCase()) || + element.originalProjectProposal.toLowerCase().contains(searchText.toLowerCase()) || + element.report.toLowerCase().contains(searchText.toLowerCase()), + ) .toList(); } - setState(() {}); + + setState(() { + _resetValueIfNotValid(); + }); } + Future _refresh() async { - // Fetch data for the next year based on the currently selected year setState(() { initializeProjectLists(); + selectedOrganizations = ['All']; + selectedProposals = ['All']; selectedYear = 2023; - if (selectedYear > 2023) - selectedYear = 2019; // Reset to the beginning if it exceeds 2023 + if (selectedYear > 2023) selectedYear = 2019; // Reset to the beginning if it exceeds 2023 + }); + } + + + void resetProjectsByLanguage() { + switch (selectedYear) { + case 2019: + projectList = gsod2019; + break; + case 2020: + projectList = gsod2020; + break; + case 2021: + projectList = gsod2021; + break; + case 2022: + projectList = gsod2022; + break; + case 2023: + projectList = gsod2023; + break; + case 2024: + projectList = gsod2023; + break; + } + filterProjects(); + } + + + void filterProjects() { + var filteredProjects = List.from(projectList); + print("!@# $selectedOrganizations"); + + + if (!selectedOrganizations.contains('All')) { + filteredProjects = filteredProjects.where((project) { + return selectedOrganizations.contains(project.organizationName); + }).toList(); + }else{ + filteredProjects = gsod2023; + } + + + if (!selectedProposals.contains('All')) { + filteredProjects = filteredProjects.where((project) { + return selectedProposals.contains(project.acceptedProjectProposal); + }).toList(); + } + + + + + + + setState(() { + projectList = filteredProjects; + _resetValueIfNotValid(); }); } + + void _resetValueIfNotValid() { + // Reset selectedOrganizations if they are not valid + var validOrganizations = projectList.map((project) => project.organizationName).toSet(); + selectedOrganizations = selectedOrganizations.where((org) => validOrganizations.contains(org) || org == 'All').toList(); + if (selectedOrganizations.isEmpty) { + selectedOrganizations = ['All']; + } + + + // Reset selectedProposals if they are not valid + var validProposals = projectList.map((project) => project.acceptedProjectProposal).toSet(); + selectedProposals = selectedProposals.where((proposal) => validProposals.contains(proposal) || proposal == 'All').toList(); + if (selectedProposals.isEmpty) { + selectedProposals = ['All']; + } + } + + @override Widget build(BuildContext context) { - // var height = MediaQuery.sizeOf(context).height; - // var width = MediaQuery.sizeOf(context).width; - ScreenUtil.init(context); + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + var height = MediaQuery.sizeOf(context).height; + var width = MediaQuery.sizeOf(context).width; return RefreshIndicator( onRefresh: _refresh, child: Scaffold( - appBar: AppBar(title: const Text('OpSo'), actions: [ + appBar: AppBar(title: const Text('GSoD'), actions: [ IconButton( icon: (isBookmarked) ? const Icon(Icons.bookmark_add_rounded) @@ -183,39 +244,34 @@ class _GoogleSeasonOfDocsScreenState extends State { }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: Text( - isBookmarked ? 'Bookmark added' : 'Bookmark removed'), - duration: const Duration( - seconds: 2), // Adjust the duration as needed + content: Text(isBookmarked ? 'Bookmark added' : 'Bookmark removed'), + duration: const Duration(seconds: 2), // Adjust the duration as needed ), ); if (isBookmarked) { - print("Adding"); HandleBookmark.addBookmark(currentProgram, currentPage); } else { - print("Deleting"); HandleBookmark.deleteBookmark(currentProgram); } }, ) ]), body: FutureBuilder( - future: getProjectFunction, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } else if (snapshot.connectionState == ConnectionState.done) { - return Padding( - padding: EdgeInsets.symmetric( - horizontal: ScreenUtil().setWidth(46), - vertical: ScreenUtil().setHeight(16)), + future: getProjectFunction, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } else if (snapshot.connectionState == ConnectionState.done) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 46, vertical: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisSize: MainAxisSize.min, children: [ TextFormField( decoration: InputDecoration( filled: true, - // fillColor: const Color(0xFFEEEEEE), hintText: 'Search', suffixIcon: const Icon(Icons.search), enabledBorder: OutlineInputBorder( @@ -242,12 +298,10 @@ class _GoogleSeasonOfDocsScreenState extends State { color: Color(0xFFEEEEEE), ), ), - contentPadding: EdgeInsets.symmetric( - vertical: ScreenUtil().setHeight(12), - horizontal: ScreenUtil().setWidth(40)), + contentPadding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 20.0), ), onFieldSubmitted: (value) { - print("value is $value"); search(value.trim()); }, onChanged: (value) { @@ -256,16 +310,15 @@ class _GoogleSeasonOfDocsScreenState extends State { } }, ), - SizedBox(height: ScreenUtil().setHeight(20)), + const SizedBox(height: 20), Container( constraints: BoxConstraints( - maxHeight: ScreenUtil().screenHeight * 0.3, + maxHeight: height * 0.3, ), - width: ScreenUtil().screenWidth, + width: width, child: GridView( physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, childAspectRatio: 1.5 / 0.6, crossAxisSpacing: 15, @@ -274,11 +327,12 @@ class _GoogleSeasonOfDocsScreenState extends State { children: [ YearButton( year: "2019", - isEnabled: selectedYear == 2019 ? true : false, + isEnabled: selectedYear == 2019, onTap: () { setState(() { projectList = gsod2019; selectedYear = 2019; + _resetValueIfNotValid(); }); }, backgroundColor: selectedYear == 2019 @@ -287,11 +341,12 @@ class _GoogleSeasonOfDocsScreenState extends State { ), YearButton( year: "2020", - isEnabled: selectedYear == 2020 ? true : false, + isEnabled: selectedYear == 2020, onTap: () { setState(() { projectList = gsod2020; selectedYear = 2020; + _resetValueIfNotValid(); }); }, backgroundColor: selectedYear == 2020 @@ -300,11 +355,12 @@ class _GoogleSeasonOfDocsScreenState extends State { ), YearButton( year: "2021", - isEnabled: selectedYear == 2021 ? true : false, + isEnabled: selectedYear == 2021, onTap: () { setState(() { projectList = gsod2021; selectedYear = 2021; + _resetValueIfNotValid(); }); }, backgroundColor: selectedYear == 2021 @@ -313,11 +369,12 @@ class _GoogleSeasonOfDocsScreenState extends State { ), YearButton( year: "2022", - isEnabled: selectedYear == 2022 ? true : false, + isEnabled: selectedYear == 2022, onTap: () { setState(() { projectList = gsod2022; selectedYear = 2022; + _resetValueIfNotValid(); }); }, backgroundColor: selectedYear == 2022 @@ -326,11 +383,12 @@ class _GoogleSeasonOfDocsScreenState extends State { ), YearButton( year: "2023", - isEnabled: selectedYear == 2023 ? true : false, + isEnabled: selectedYear == 2023, onTap: () { setState(() { projectList = gsod2023; selectedYear = 2023; + _resetValueIfNotValid(); }); }, backgroundColor: selectedYear == 2023 @@ -340,62 +398,113 @@ class _GoogleSeasonOfDocsScreenState extends State { ], ), ), - // const SizedBox(height: 20), - // SizedBox( - // height: 50, - // child: ElevatedButton( - // onPressed: () {}, - // style: ElevatedButton.styleFrom( - // shape: const RoundedRectangleBorder( - // borderRadius: BorderRadius.zero, - // ), - // backgroundColor: const Color.fromARGB( - // 255, 253, 214, 115), // Set button color - // padding: const EdgeInsets.symmetric( - // vertical: 10.0, horizontal: 20.0), - // ), - // child: const Text( - // 'View Projects', - // style: TextStyle(color: Colors.white, fontSize: 18), - // ), - // ), - // ), - const SizedBox( - height: 20, + const SizedBox(height: 20), + MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: [ + MultiSelectItem('All', 'All'), + ...projectList + .map((project) => project.organizationName) + .toSet() + .map((organization) { + return MultiSelectItem(organization, organization); + }).toList(), + ], + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), + ), + title: Text("Select Organizations", style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: const Text("Filter by Organization"), + onConfirm: (results) { + setState(() { + print("results are $results"); + if (results.contains('All')) { + selectedOrganizations = ['All']; + } else { + selectedOrganizations = results.isNotEmpty ? results : ['All']; + } + + + print("this is selected organization $selectedOrganizations"); + filterProjects(); + }); + }, + initialValue: selectedOrganizations, ), - Expanded( - // width: width, - child: ListView.builder( - itemCount: projectList.length, - itemBuilder: (BuildContext context, int index) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: selectedYear <= 2020 - ? GsodProjectWidgetOld( - index: index + 1, - modal: projectList[index], - height: ScreenUtil().screenHeight * 0.2, - width: ScreenUtil().screenWidth, - ) - : GsodProjectWidgetNew( - index: index + 1, - modal: projectList[index], - height: ScreenUtil().screenHeight * 0.2, - width: ScreenUtil().screenWidth, - ), - ); - }, + + const SizedBox(height: 20), + MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: [ + MultiSelectItem('All', 'All'), + ...projectList + .map((project) => project.acceptedProjectProposal) + .toSet() + .map((proposal) { + return MultiSelectItem(proposal, proposal); + }).toList(), + ], + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), ), + title: Text("Select Proposals", style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: const Text("Filter by Accepted Proposal"), + onConfirm: (results) { + setState(() { + print("results are $results"); + if (results.contains('All')) { + selectedProposals = ['All']; + } else { + selectedProposals = results.isNotEmpty ? results : ['All']; + } + + + print("this is selected proposal $selectedProposals"); + filterProjects(); + }); + }, + initialValue: selectedProposals, + ), + + + const SizedBox(height: 20), + ListView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: projectList.length, + itemBuilder: (BuildContext context, int index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: selectedYear <= 2020 + ? GsodProjectWidgetOld( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ) + : GsodProjectWidgetNew( + index: index + 1, + modal: projectList[index], + height: height * 0.2, + width: width, + ), + ); + }, ), ], ), - ); - } else { - return const Center(child: Text("Some error occured")); - } - }), + ), + ); + } else { + return const Center(child: Text("Some error occurred")); + } + }, + ), ), ); } } + diff --git a/lib/programs screen/google_summer_of_code_screen.dart b/lib/programs screen/google_summer_of_code_screen.dart index ff97c20..273e81a 100644 --- a/lib/programs screen/google_summer_of_code_screen.dart +++ b/lib/programs screen/google_summer_of_code_screen.dart @@ -1,31 +1,29 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:opso/widgets/gsoc/GsocProjectWidget.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:multi_select_flutter/multi_select_flutter.dart'; import '../modals/GSoC/Gsoc.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - -import '../modals/book_mark_model.dart'; import '../services/ApiService.dart'; import '../widgets/SearchandFilterWidget.dart'; import '../widgets/year_button.dart'; + class GoogleSummerOfCodeScreen extends StatefulWidget { @override State createState() => _GoogleSummerOfCodeScreenState(); } + class _GoogleSummerOfCodeScreenState extends State { + String selectedOrg = ''; // Ensure this is defined List gsoc2024 = []; List gsoc2023 = []; List gsoc2022 = []; List gsoc2021 = []; - String currectPage = "/google_summer_of_code"; - String currentProject = "Google Summer of Code"; - bool isBookmarked = true; int selectedYear = 2024; - List languages = [ 'js', 'python', @@ -44,22 +42,19 @@ class _GoogleSummerOfCodeScreenState extends State { 'Dart' ]; List orgList = []; + List selectedLanguages = []; + List allOrganizations = []; + List selectedOrganizations = []; late Future _dataFetchFuture; - final GlobalKey _listKey = GlobalKey(); + @override void initState() { super.initState(); + _refresh(); _dataFetchFuture = getProjectData(); - _checkBookmarkStatus(); } - Future _checkBookmarkStatus() async { - bool bookmarkStatus = await HandleBookmark.isBookmarked(currentProject); - setState(() { - isBookmarked = bookmarkStatus; - }); - } Future getProjectData() async { ApiService apiService = ApiService(); @@ -69,65 +64,59 @@ class _GoogleSummerOfCodeScreenState extends State { Gsoc orgData2023 = await apiService.getOrgByYear('2023'); Gsoc orgData2024 = await apiService.getOrgByYear('2024'); + setState(() { gsoc2021 = orgData2021.organizations ?? []; gsoc2022 = orgData2022.organizations ?? []; gsoc2023 = orgData2023.organizations ?? []; gsoc2024 = orgData2024.organizations ?? []; orgList = gsoc2024; // Default to the latest year + allOrganizations = [...orgList.map((org) => org.name!).toSet()]; }); - _populateAnimatedList(orgList); } catch (e) { print('Error: $e'); } } - void _clearAnimatedList() { - for (var i = orgList.length - 1; i >= 0; i--) { - _listKey.currentState?.removeItem( - i, - (context, animation) => SizedBox.shrink(), - duration: Duration.zero, - ); - } - } - void _populateAnimatedList(List organizations) { - for (var i = 0; i < organizations.length; i++) { - _listKey.currentState?.insertItem(i); + void filterProjects() { + orgList = _getOrganizationsByYear(selectedYear); + if(selectedLanguages.length>=2){ + selectedLanguages.removeAt(0); + } + if(selectedOrganizations.length>=2){ + selectedOrganizations.removeAt(0); + } + if (!selectedLanguages.contains('All')) { + orgList = orgList + .where((project) => + project.technologies?.any(selectedLanguages.contains) == true) + .toList(); } - } - void searchTag(String searchTag) { - setState(() { - _clearAnimatedList(); - orgList = _getOrganizationsByYear(selectedYear) - .where((element) => - element.technologies?.contains(searchTag) == true || - element.topics?.contains(searchTag) == true) + + if (!selectedOrganizations.contains('All')) { + orgList = orgList + .where((project) => selectedOrganizations.contains(project.name)) .toList(); - _populateAnimatedList(orgList); - }); - } + } - void search(String searchText) { - setState(() { - _clearAnimatedList(); - if (searchText.isEmpty) { - orgList = _getOrganizationsByYear(selectedYear); - } else { - orgList = _getOrganizationsByYear(selectedYear) - .where((element) => - element.name - ?.toLowerCase() - .contains(searchText.toLowerCase()) == - true) - .toList(); - } - _populateAnimatedList(orgList); - }); + + // Update organization filter based on selected languages + allOrganizations = [ + ..._getOrganizationsByYear(selectedYear) + .where((org) => + selectedLanguages.contains('All') || + org.technologies?.any(selectedLanguages.contains) == true) + .map((org) => org.name!) + .toSet() + ]; + + + setState(() {}); } + List _getOrganizationsByYear(int year) { switch (year) { case 2021: @@ -143,52 +132,49 @@ class _GoogleSummerOfCodeScreenState extends State { } } + Future _refresh() async { + await getProjectData(); setState(() { - _dataFetchFuture = getProjectData(); selectedYear = 2024; + selectedLanguages = ['All']; + selectedOrganizations = ['All']; + filterProjects(); + }); + } + + + // Add this method to the _GoogleSummerOfCodeScreenState class + void search(String searchText) { + setState(() { + selectedOrg = 'All'; // Reset selectedOrg to avoid mismatch + if (searchText.isEmpty) { + orgList = _getOrganizationsByYear(selectedYear); + } else { + orgList = _getOrganizationsByYear(selectedYear) + .where((element) => + element.name + ?.toLowerCase() + .contains(searchText.toLowerCase()) == + true) + .toList(); + } }); } + @override Widget build(BuildContext context) { - // var height = MediaQuery.of(context).size.height; - // var width = MediaQuery.of(context).size.width; + var height = MediaQuery.of(context).size.height; + var width = MediaQuery.of(context).size.width; - ScreenUtil.init( - context, - ); return RefreshIndicator( onRefresh: _refresh, child: Scaffold( - appBar: AppBar(title: const Text('Google Summer of Code'), actions: [ - IconButton( - icon: (isBookmarked) - ? const Icon(Icons.bookmark_add_rounded) - : const Icon(Icons.bookmark_add_outlined), - onPressed: () { - setState(() { - isBookmarked = !isBookmarked; - }); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - isBookmarked ? 'Bookmark added' : 'Bookmark removed'), - duration: const Duration( - seconds: 2), // Adjust the duration as needed - ), - ); - if (isBookmarked) { - print("Adding"); - HandleBookmark.addBookmark(currentProject, currectPage); - } else { - print("Deleting"); - HandleBookmark.deleteBookmark(currentProject); - } - }, - ) - ]), + appBar: AppBar( + title: Text('Google Summer of Code'), + ), body: FutureBuilder( future: _dataFetchFuture, builder: (context, snapshot) { @@ -197,173 +183,206 @@ class _GoogleSummerOfCodeScreenState extends State { } else if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } else { - return Padding( - padding: EdgeInsets.symmetric( - horizontal: ScreenUtil().setWidth(46), - vertical: ScreenUtil().setHeight(16)), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - TextFormField( - decoration: InputDecoration( - filled: true, - hintText: 'Search', - suffixIcon: const Icon(Icons.search), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + return SingleChildScrollView( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 46, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + TextFormField( + decoration: InputDecoration( + filled: true, + hintText: 'Search', + suffixIcon: const Icon(Icons.search), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), - ), - disabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + disabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: const BorderSide( - color: Color(0xFFEEEEEE), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: const BorderSide( + color: Color(0xFFEEEEEE), + ), ), + contentPadding: const EdgeInsets.symmetric( + vertical: 12.0, horizontal: 20.0), ), - contentPadding: EdgeInsets.symmetric( - vertical: ScreenUtil().setWidth(12), - horizontal: ScreenUtil().setHeight(20)), + onFieldSubmitted: (value) { + search(value.trim()); + }, + onChanged: (value) { + if (value.isEmpty) { + search(value); + } + }, ), - onFieldSubmitted: (value) { - search(value.trim()); - }, - onChanged: (value) { - if (value.isEmpty) { - search(value); - } - }, - ), - SizedBox(height: ScreenUtil().setHeight(20)), - SizedBox( - height: ScreenUtil().screenHeight * 0.2, - width: ScreenUtil().screenWidth, - child: GridView( - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - childAspectRatio: 1.5 / 0.6, - crossAxisSpacing: 15, - mainAxisSpacing: 15, + const SizedBox(height: 20), + SizedBox( + height: height * 0.2, + width: width, + child: GridView( + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 1.5 / 0.6, + crossAxisSpacing: 15, + mainAxisSpacing: 15, + ), + children: [ + YearButton( + year: "2021", + isEnabled: selectedYear == 2021, + onTap: () { + setState(() { + selectedYear = 2021; + selectedLanguages = []; + selectedOrganizations = []; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2021 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2022", + isEnabled: selectedYear == 2022, + onTap: () { + setState(() { + selectedYear = 2022; + selectedLanguages = []; + selectedOrganizations = []; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2022 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + year: "2023", + isEnabled: selectedYear == 2023, + onTap: () { + setState(() { + selectedYear = 2023; + selectedLanguages = []; + selectedOrganizations = []; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2023 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + YearButton( + isEnabled: selectedYear == 2024, + year: "2024", + onTap: () { + setState(() { + selectedYear = 2024; + selectedLanguages = []; + selectedOrganizations = []; + filterProjects(); + }); + }, + backgroundColor: selectedYear == 2024 + ? Colors.white + : const Color.fromRGBO(255, 183, 77, 1), + ), + ], ), + ), + const SizedBox(height: 20), + _buildMultiSelectField( + items: languages, + selectedValues: selectedLanguages, + title: "Select Languages", + buttonText: "Filter by Language", + onConfirm: (results) { + setState(() { + selectedLanguages = + results.isNotEmpty ? results : []; + print(selectedLanguages); + filterProjects(); + }); + }, + ), + const SizedBox(height: 20), + _buildMultiSelectField( + items: allOrganizations, + selectedValues: selectedOrganizations, + title: "Select Organizations", + buttonText: "Filter by Organization", + onConfirm: (results) { + setState(() { + selectedOrganizations = + results.isNotEmpty ? results : []; + print(selectedOrganizations); + filterProjects(); + }); + }, + ), + const SizedBox(height: 20), + orgList.isEmpty + ? Column( children: [ - YearButton( - year: "2021", - isEnabled: selectedYear == 2021, - onTap: () { - setState(() { - selectedYear = 2021; - _clearAnimatedList(); - orgList = gsoc2021; - }); - _populateAnimatedList(gsoc2021); - }, - backgroundColor: selectedYear == 2021 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2022", - isEnabled: selectedYear == 2022, - onTap: () { - setState(() { - selectedYear = 2022; - _clearAnimatedList(); - orgList = gsoc2022; - }); - _populateAnimatedList(gsoc2022); - }, - backgroundColor: selectedYear == 2022 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - year: "2023", - isEnabled: selectedYear == 2023, - onTap: () { - setState(() { - selectedYear = 2023; - _clearAnimatedList(); - orgList = gsoc2023; - }); - _populateAnimatedList(gsoc2023); - }, - backgroundColor: selectedYear == 2023 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), - ), - YearButton( - isEnabled: selectedYear == 2024, - year: "2024", - onTap: () { - setState(() { - selectedYear = 2024; - _clearAnimatedList(); - orgList = gsoc2024; - }); - _populateAnimatedList(gsoc2024); - }, - backgroundColor: selectedYear == 2024 - ? Colors.white - : const Color.fromRGBO(255, 183, 77, 1), + const Center(child: Text('No projects found')), + const SizedBox(height: 20), + TextButton( + onPressed: _refresh, + child: const Text('Refresh'), ), ], - ), - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Text( - 'Filter by Language:', - style: TextStyle(fontWeight: FontWeight.w400), - ), - Padding( - padding: EdgeInsets.all(ScreenUtil().setWidth(8)), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - DropdownWidget( - items: languages, - hintText: 'Language', - onChanged: (newValue) { - searchTag(newValue); - }, + ) + : Container( + height: height, + child: ListView.builder( + itemCount: orgList.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + spreadRadius: 2, + blurRadius: 5, + offset: Offset(0, 3), // changes position of shadow + ), + ], + ), + child: GsocProjectWidget( + index: index + 1, + modal: orgList[index], + height: height * 0.2, + width: width, + ), ), - ], - ), - ) - ], - ), - Expanded( - child: AnimatedList( - key: _listKey, - initialItemCount: orgList.length, - itemBuilder: (context, index, animation) { - return _buildAnimatedItem( - context, - index, - animation, - ScreenUtil().screenHeight, - ScreenUtil().screenWidth); - }, + ); + }, + ), ), - ), - ], + ], + ), ), ); } @@ -373,44 +392,35 @@ class _GoogleSummerOfCodeScreenState extends State { ); } - Widget _buildAnimatedItem(BuildContext context, int index, - Animation animation, double height, double width) { - return SizeTransition( - sizeFactor: animation, - axis: Axis.vertical, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(20), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.5), - spreadRadius: 2, - blurRadius: 5, - offset: Offset(0, 3), // changes position of shadow - ), - ], - ), - child: Column( - children: [ - GsocProjectWidget( - index: index + 1, - modal: orgList[index], - height: height * 0.2, - width: width, - ), - ], - ), - ), + + Widget _buildMultiSelectField({ + required List items, + required List selectedValues, + required String title, + required String buttonText, + required void Function(List) onConfirm, + }) { + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + return MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: items.map((e) => MultiSelectItem(e, e)).toList(), + initialValue: selectedValues, + title: Text(title, + style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), + buttonText: Text(buttonText), + onConfirm: onConfirm, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), ), ); } } + void main() { runApp(MaterialApp( home: GoogleSummerOfCodeScreen(), )); } + diff --git a/pubspec.lock b/pubspec.lock index 65f4ec1..994a129 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -260,26 +260,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.0.1" lints: dependency: transitive description: @@ -308,10 +308,18 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + multi_select_flutter: + dependency: "direct main" + description: + name: multi_select_flutter + sha256: "503857b415d390d29159df8a9d92d83c6aac17aaf1c307fb7bcfc77d097d20ed" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "4.1.3" path: dependency: transitive description: @@ -481,10 +489,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.6.1" timeline_tile: dependency: "direct main" description: @@ -601,10 +609,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "13.0.0" web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 40931d4..0ed0700 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: timeline_tile: ^2.0.0 flutter_svg: ^2.0.10+1 flutter_screenutil: ^5.9.1 - + multi_select_flutter: ^4.1.3 dev_dependencies: awesome_notifications: ^0.9.3+1 flutter_launcher_icons: ^0.13.1 From c49c0316f8dbf4a7dfc4424779e697c37fde9354 Mon Sep 17 00:00:00 2001 From: Saumya-28 Date: Tue, 4 Jun 2024 19:17:51 +0530 Subject: [PATCH 4/4] Updated Multi-Select Filter Feature as suggested --- lib/programs screen/girl_script.dart | 31 ++- .../google_season_of_docs_screen.dart | 183 +++++++++++------- .../google_summer_of_code_screen.dart | 89 ++++++--- 3 files changed, 185 insertions(+), 118 deletions(-) diff --git a/lib/programs screen/girl_script.dart b/lib/programs screen/girl_script.dart index c9d0409..4dcef93 100644 --- a/lib/programs screen/girl_script.dart +++ b/lib/programs screen/girl_script.dart @@ -1,6 +1,8 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:multi_select_flutter/dialog/multi_select_dialog_field.dart'; +import 'package:multi_select_flutter/util/multi_select_item.dart'; import 'package:opso/modals/book_mark_model.dart'; import 'package:opso/modals/gssoc_project_modal.dart'; import 'package:opso/widgets/gssoc_project_widget.dart'; @@ -8,9 +10,6 @@ import 'package:opso/widgets/year_button.dart'; import '../widgets/SearchandFilterWidget.dart'; -import 'package:multi_select_flutter/multi_select_flutter.dart'; - - class GSSOCScreen extends StatefulWidget { const GSSOCScreen({super.key}); @@ -117,13 +116,15 @@ class _GSSOCScreenState extends State { void filterProjects() { - // Filter projects by year first (if applicable) + // Filter projects by year first projectList = _getProjectsByYear(); // Filter projects by selected languages if (!selectedLanguages.contains('All')) { - projectList = projectList.where((project) => project.techstack.any(selectedLanguages.contains)).toList(); + projectList = projectList.where((project) => + selectedLanguages.every((language) => project.techstack.contains(language)) + ).toList(); } @@ -140,6 +141,10 @@ class _GSSOCScreenState extends State { // Ensure state is updated to reflect changes setState(() {}); } + + + + void _updateOrganizationList() { allOrganizations = _extractUniqueValues((project) => project.hostedBy) .where((organization) => projectList.any((project) => project.hostedBy == organization)) @@ -230,24 +235,12 @@ class _GSSOCScreenState extends State { onConfirm: (results) { setState(() { selectedLanguages = results.isNotEmpty ? results : ['All']; + print(selectedLanguages); filterProjects(); }); }, ), const SizedBox(height: 20), - _buildMultiSelectField( - items: allOrganizations, - selectedValues: selectedOrganizations, - title: "Select Organizations", - buttonText: "Filter by Name", - onConfirm: (results) { - setState(() { - selectedOrganizations = results.isNotEmpty ? results : ['All']; - print("Selected Organizations: $selectedOrganizations"); - filterProjects(); - }); - }, - ), const SizedBox(height: 20), _buildProjectList(height, width), ], @@ -410,7 +403,5 @@ class _GSSOCScreenState extends State { ), ); } - - } diff --git a/lib/programs screen/google_season_of_docs_screen.dart b/lib/programs screen/google_season_of_docs_screen.dart index 8852191..8c66f0a 100644 --- a/lib/programs screen/google_season_of_docs_screen.dart +++ b/lib/programs screen/google_season_of_docs_screen.dart @@ -10,6 +10,8 @@ import 'package:opso/widgets/gsod/gsod_project_widget_old.dart'; import 'package:opso/widgets/year_button.dart'; + + class GoogleSeasonOfDocsScreen extends StatefulWidget { @override State createState() => @@ -17,8 +19,10 @@ class GoogleSeasonOfDocsScreen extends StatefulWidget { } + + class _GoogleSeasonOfDocsScreenState extends State { - List selectedOrganizations = ['All']; + List selectedOrganizations = []; List selectedProposals = ['All']; String currentProgram = "Google Season of Docs"; bool isBookmarked = true; @@ -31,10 +35,15 @@ class _GoogleSeasonOfDocsScreenState extends State { int selectedYear = 2023; + + List projectList = []; + List allProjectList =[]; Future? getProjectFunction; + + Future initializeProjectLists() async { String response = await rootBundle.loadString('assets/projects/gsod/gsod2023.json'); @@ -43,6 +52,7 @@ class _GoogleSeasonOfDocsScreenState extends State { gsod2023.add(GsodModalNew.fromMap(data)); } projectList = List.from(gsod2023); + allProjectList = List.from(gsod2023); response = @@ -53,6 +63,8 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + response = await rootBundle.loadString('assets/projects/gsod/gsod2021.json'); jsonList = await json.decode(response); @@ -61,6 +73,8 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + response = await rootBundle.loadString('assets/projects/gsod/gsod2020.json'); jsonList = await json.decode(response); @@ -69,6 +83,8 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + response = await rootBundle.loadString('assets/projects/gsod/gsod2019.json'); jsonList = await json.decode(response); @@ -78,6 +94,8 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + @override void initState() { getProjectFunction = initializeProjectLists(); @@ -86,6 +104,8 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + Future _checkBookmarkStatus() async { bool bookmarkStatus = await HandleBookmark.isBookmarked(currentProgram); setState(() { @@ -94,6 +114,8 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + void search(String searchText) { if (searchText.isEmpty) { resetProjectsByLanguage(); @@ -133,12 +155,16 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + setState(() { _resetValueIfNotValid(); }); } + + Future _refresh() async { setState(() { initializeProjectLists(); @@ -150,45 +176,57 @@ class _GoogleSeasonOfDocsScreenState extends State { } + + void resetProjectsByLanguage() { switch (selectedYear) { case 2019: projectList = gsod2019; + allProjectList = gsod2019; break; case 2020: projectList = gsod2020; + allProjectList = gsod2020; break; case 2021: projectList = gsod2021; + allProjectList = gsod2021; break; case 2022: projectList = gsod2022; + allProjectList = gsod2022; break; case 2023: projectList = gsod2023; + allProjectList = gsod2023; break; case 2024: projectList = gsod2023; + allProjectList = gsod2023; break; } filterProjects(); } + + void filterProjects() { var filteredProjects = List.from(projectList); print("!@# $selectedOrganizations"); + // Filter by organizations if (!selectedOrganizations.contains('All')) { filteredProjects = filteredProjects.where((project) { - return selectedOrganizations.contains(project.organizationName); + return selectedOrganizations.every((org) => project.organizationName.contains(org)); }).toList(); - }else{ + } else { filteredProjects = gsod2023; } + // Filter by proposals if (!selectedProposals.contains('All')) { filteredProjects = filteredProjects.where((project) { return selectedProposals.contains(project.acceptedProjectProposal); @@ -196,35 +234,33 @@ class _GoogleSeasonOfDocsScreenState extends State { } - - - - setState(() { projectList = filteredProjects; - _resetValueIfNotValid(); + /*_resetValueIfNotValid();*/ }); } + + + + void _resetValueIfNotValid() { // Reset selectedOrganizations if they are not valid var validOrganizations = projectList.map((project) => project.organizationName).toSet(); selectedOrganizations = selectedOrganizations.where((org) => validOrganizations.contains(org) || org == 'All').toList(); - if (selectedOrganizations.isEmpty) { - selectedOrganizations = ['All']; - } + /* if (selectedOrganizations.isEmpty) { + selectedOrganizations = ['All']; + }*/ + + - // Reset selectedProposals if they are not valid - var validProposals = projectList.map((project) => project.acceptedProjectProposal).toSet(); - selectedProposals = selectedProposals.where((proposal) => validProposals.contains(proposal) || proposal == 'All').toList(); - if (selectedProposals.isEmpty) { - selectedProposals = ['All']; - } } + + @override Widget build(BuildContext context) { bool isDarkMode = Theme.of(context).brightness == Brightness.dark; @@ -399,79 +435,30 @@ class _GoogleSeasonOfDocsScreenState extends State { ), ), const SizedBox(height: 20), - MultiSelectDialogField( - backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + _buildMultiSelectField( items: [ - MultiSelectItem('All', 'All'), + 'All', ...projectList .map((project) => project.organizationName) - .toSet() - .map((organization) { - return MultiSelectItem(organization, organization); - }).toList(), + .toSet(), ], - decoration: BoxDecoration( - border: Border.all(color: Colors.grey), - borderRadius: BorderRadius.circular(10), - ), - title: Text("Select Organizations", style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), - buttonText: const Text("Filter by Organization"), + selectedValues: selectedOrganizations, + title: "Select Organizations", + buttonText: "Filter by Organization", onConfirm: (results) { setState(() { - print("results are $results"); - if (results.contains('All')) { - selectedOrganizations = ['All']; - } else { - selectedOrganizations = results.isNotEmpty ? results : ['All']; - } - - + selectedOrganizations = results.isNotEmpty ? results : ['All']; print("this is selected organization $selectedOrganizations"); filterProjects(); }); }, - initialValue: selectedOrganizations, ), const SizedBox(height: 20), - MultiSelectDialogField( - backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, - items: [ - MultiSelectItem('All', 'All'), - ...projectList - .map((project) => project.acceptedProjectProposal) - .toSet() - .map((proposal) { - return MultiSelectItem(proposal, proposal); - }).toList(), - ], - decoration: BoxDecoration( - border: Border.all(color: Colors.grey), - borderRadius: BorderRadius.circular(10), - ), - title: Text("Select Proposals", style: TextStyle(color: isDarkMode ? Colors.black : Colors.black)), - buttonText: const Text("Filter by Accepted Proposal"), - onConfirm: (results) { - setState(() { - print("results are $results"); - if (results.contains('All')) { - selectedProposals = ['All']; - } else { - selectedProposals = results.isNotEmpty ? results : ['All']; - } - - - print("this is selected proposal $selectedProposals"); - filterProjects(); - }); - }, - initialValue: selectedProposals, - ), - - const SizedBox(height: 20), - ListView.builder( + projectList.isNotEmpty + ? ListView.builder( shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: projectList.length, @@ -493,7 +480,25 @@ class _GoogleSeasonOfDocsScreenState extends State { ), ); }, + ) + : Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('No projects available.'), + SizedBox(height: 20), + ElevatedButton( + onPressed: () { + // Call the refresh function + _refresh(); + }, + child: Text('Refresh'), + ), + ], + ), ), + + ], ), ), @@ -506,5 +511,33 @@ class _GoogleSeasonOfDocsScreenState extends State { ), ); } + Widget _buildMultiSelectField({ + required List items, + required List selectedValues, + required String title, + required String buttonText, + required void Function(List) onConfirm, + }) { + bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + return MultiSelectDialogField( + backgroundColor: isDarkMode ? Colors.grey.shade100 : Colors.white, + items: items.map((e) => MultiSelectItem(e, e)).toList(), + initialValue: selectedValues, + title: Text( + title, + style: TextStyle(color: isDarkMode ? Colors.black : Colors.black), + ), + buttonText: Text(buttonText), + onConfirm: onConfirm, + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(10), + ), + ); + } + + } + + diff --git a/lib/programs screen/google_summer_of_code_screen.dart b/lib/programs screen/google_summer_of_code_screen.dart index 273e81a..770fc11 100644 --- a/lib/programs screen/google_summer_of_code_screen.dart +++ b/lib/programs screen/google_summer_of_code_screen.dart @@ -1,6 +1,5 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:opso/widgets/gsoc/GsocProjectWidget.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:multi_select_flutter/multi_select_flutter.dart'; @@ -10,6 +9,8 @@ import '../widgets/SearchandFilterWidget.dart'; import '../widgets/year_button.dart'; + + class GoogleSummerOfCodeScreen extends StatefulWidget { @override State createState() => @@ -17,7 +18,10 @@ class GoogleSummerOfCodeScreen extends StatefulWidget { } + + class _GoogleSummerOfCodeScreenState extends State { + bool _isRefreshing = false; String selectedOrg = ''; // Ensure this is defined List gsoc2024 = []; List gsoc2023 = []; @@ -27,7 +31,7 @@ class _GoogleSummerOfCodeScreenState extends State { List languages = [ 'js', 'python', - 'React', + 'django', 'Angular', 'Bootstrap', 'Firebase', @@ -37,7 +41,7 @@ class _GoogleSummerOfCodeScreenState extends State { 'Next', 'css', 'html', - 'javaScript', + 'javascript', 'flutter', 'Dart' ]; @@ -48,6 +52,8 @@ class _GoogleSummerOfCodeScreenState extends State { late Future _dataFetchFuture; + + @override void initState() { super.initState(); @@ -56,6 +62,8 @@ class _GoogleSummerOfCodeScreenState extends State { } + + Future getProjectData() async { ApiService apiService = ApiService(); try { @@ -65,6 +73,8 @@ class _GoogleSummerOfCodeScreenState extends State { Gsoc orgData2024 = await apiService.getOrgByYear('2024'); + + setState(() { gsoc2021 = orgData2021.organizations ?? []; gsoc2022 = orgData2022.organizations ?? []; @@ -79,6 +89,8 @@ class _GoogleSummerOfCodeScreenState extends State { } + + void filterProjects() { orgList = _getOrganizationsByYear(selectedYear); if(selectedLanguages.length>=2){ @@ -88,13 +100,16 @@ class _GoogleSummerOfCodeScreenState extends State { selectedOrganizations.removeAt(0); } if (!selectedLanguages.contains('All')) { - orgList = orgList - .where((project) => - project.technologies?.any(selectedLanguages.contains) == true) - .toList(); + orgList = orgList.where((project) => + selectedLanguages.every((language) => project.technologies?.contains(language) == true) + ).toList(); } + + + + if (!selectedOrganizations.contains('All')) { orgList = orgList .where((project) => selectedOrganizations.contains(project.name)) @@ -102,6 +117,8 @@ class _GoogleSummerOfCodeScreenState extends State { } + + // Update organization filter based on selected languages allOrganizations = [ ..._getOrganizationsByYear(selectedYear) @@ -113,10 +130,14 @@ class _GoogleSummerOfCodeScreenState extends State { ]; + + setState(() {}); } + + List _getOrganizationsByYear(int year) { switch (year) { case 2021: @@ -133,17 +154,25 @@ class _GoogleSummerOfCodeScreenState extends State { } + + Future _refresh() async { + setState(() { + _isRefreshing = true; + }); await getProjectData(); setState(() { selectedYear = 2024; selectedLanguages = ['All']; selectedOrganizations = ['All']; filterProjects(); + _isRefreshing = false; }); } + + // Add this method to the _GoogleSummerOfCodeScreenState class void search(String searchText) { setState(() { @@ -163,12 +192,16 @@ class _GoogleSummerOfCodeScreenState extends State { } + + @override Widget build(BuildContext context) { var height = MediaQuery.of(context).size.height; var width = MediaQuery.of(context).size.width; + + return RefreshIndicator( onRefresh: _refresh, child: Scaffold( @@ -324,28 +357,30 @@ class _GoogleSummerOfCodeScreenState extends State { }, ), const SizedBox(height: 20), - _buildMultiSelectField( - items: allOrganizations, - selectedValues: selectedOrganizations, - title: "Select Organizations", - buttonText: "Filter by Organization", - onConfirm: (results) { - setState(() { - selectedOrganizations = - results.isNotEmpty ? results : []; - print(selectedOrganizations); - filterProjects(); - }); - }, - ), - const SizedBox(height: 20), orgList.isEmpty + ? _isRefreshing ? Column( + children: const [ + Center( + child: Column( + children: [ + CircularProgressIndicator(), + SizedBox(height: 10), + Text('Refreshing...'), + ], + ) + ), + SizedBox(height: 20), + ], + ) + : Column( children: [ const Center(child: Text('No projects found')), const SizedBox(height: 20), TextButton( - onPressed: _refresh, + onPressed: () { + _refresh(); + }, child: const Text('Refresh'), ), ], @@ -381,6 +416,8 @@ class _GoogleSummerOfCodeScreenState extends State { }, ), ), + + ], ), ), @@ -393,6 +430,8 @@ class _GoogleSummerOfCodeScreenState extends State { } + + Widget _buildMultiSelectField({ required List items, required List selectedValues, @@ -418,9 +457,13 @@ class _GoogleSummerOfCodeScreenState extends State { } + + void main() { runApp(MaterialApp( home: GoogleSummerOfCodeScreen(), )); } + +