Input type filter
To add filtering functionality to your KeywordScreen, similar to the previous examples, you'll need to implement a search input field that allows users to filter keywords based on project names or URLs. Below are the specific changes needed:
- Add Search Controller and Filtered Keywords List Add these variables in your _KeywordScreenState class:
TextEditingController _searchController = TextEditingController();
List<Keyword> _filteredKeywords = []; // List to hold filtered keywords
- Update the _fetchData Method In the _fetchData method, ensure you initialize _filteredKeywords with the data fetched from the API:
Future<void> _fetchData() async {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final credentials = await _getCredentials();
final email = credentials['email'];
final orgSlug = widget.orgSlug;
// Fetch orgRoleId from secure storage and determine the role
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
setState(() {
_userRole = determineUserRole(orgRoleId); // Use the mixin to determine the role
});
if (email != null && orgSlug.isNotEmpty) {
await organizationViewModel.getUserFunction(email, orgSlug);
await organizationViewModel.getKeyword(email, orgSlug, orgRoleId.toString(), orgUserId.toString(), orgUserorgId.toString());
_filteredKeywords = organizationViewModel.my_keywords; // Initialize with full data
}
}
- Add the Filter Method Add the _filterKeywords method below your existing methods to filter the keywords based on the search input:
void _filterKeywords(String query) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final allKeywords = organizationViewModel.my_keywords;
setState(() {
_filteredKeywords = allKeywords.where((keyword) {
final projectNameLower = keyword.projectName?.toLowerCase() ?? '';
final urlLower = keyword.url?.toLowerCase() ?? '';
final queryLower = query.toLowerCase();
return projectNameLower.contains(queryLower) ||
urlLower.contains(queryLower);
}).toList();
});
}
- Update the build Method In the build method, add the search input field above the existing list of keywords.
Add the Search TextField
Insert this below the SizedBox(height: 20), line:
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: 'Search by Project Name or URL',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
_filterKeywords(value); // Call filter method on text change
},
),
),
Update the ListView.builder
Replace organizationViewModel.my_keywords.isEmpty with _filteredKeywords.isEmpty and change organizationViewModel.my_keywords.length to _filteredKeywords.length:
Expanded(
child: _filteredKeywords.isEmpty // Check filtered keywords
? Center(
child: Text('No keywords found for this organization'),
)
: ListView.builder(
itemCount: _filteredKeywords.length,
itemBuilder: (context, index) {
final keyword = _filteredKeywords[index];
return Card(
// Card content...
);
},
),
),
Summary of Changes
Search Controller
: Added a TextEditingController to manage the search input.
Filtered Keywords List
: Introduced a list to hold the filtered keywords based on the search criteria.
Filter Method
: Implemented _filterKeywords to filter keywords based on project names or URLs.
Integrated Search Field
: Added a TextField for the search input in the UI.
Updated Display Logic
: Updated the displayed keywords to reflect the filtered results.
Full code
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:provider/provider.dart';
import 'package:wizbrand/Screen/Influencer/createkeyword.dart';
import 'package:wizbrand/Screen/layout/drawer.dart';
import 'package:wizbrand/Screen/layout/rolemixins.dart';
import 'package:wizbrand/model/keyword.dart';
import 'package:wizbrand/view_modal/organization_view_model.dart';
class KeywordScreen extends StatefulWidget {
final String orgSlug;
KeywordScreen({required this.orgSlug});
@override
_KeywordScreenState createState() => _KeywordScreenState();
}
class _KeywordScreenState extends State<KeywordScreen> with DrawerMixin, RoleMixin {
String _userRole = ''; // Variable to store the role for AppBar
TextEditingController _searchController = TextEditingController();
List<Keyword> _filteredKeywords = []; // List to hold filtered keywords
@override
void initState() {
super.initState();
_fetchData(); // Fetch keyword data when the screen initializes
}
Future<void> _fetchData() async {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final credentials = await _getCredentials();
final email = credentials['email'];
final orgSlug = widget.orgSlug;
// Fetch orgRoleId from secure storage and determine the role
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
setState(() {
_userRole = determineUserRole(orgRoleId); // Use the mixin to determine the role
});
if (email != null && orgSlug.isNotEmpty) {
await organizationViewModel.getUserFunction(email, orgSlug);
await organizationViewModel.getKeyword(email, orgSlug, orgRoleId.toString(), orgUserId.toString(), orgUserorgId.toString());
_filteredKeywords = organizationViewModel.my_keywords; // Initialize with full data
}
setState(() {}); // Rebuild the widget after fetching data
}
Future<Map<String, String?>> _getCredentials() async {
final secureStorage = FlutterSecureStorage();
String? email = await secureStorage.read(key: 'email');
String? password = await secureStorage.read(key: 'password');
return {'email': email, 'password': password};
}
void _filterKeywords(String query) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final allKeywords = organizationViewModel.my_keywords;
setState(() {
_filteredKeywords = allKeywords.where((keyword) {
final projectNameLower = keyword.projectName?.toLowerCase() ?? '';
final urlLower = keyword.url?.toLowerCase() ?? '';
final queryLower = query.toLowerCase();
return projectNameLower.contains(queryLower) ||
urlLower.contains(queryLower);
}).toList();
});
}
@override
Widget build(BuildContext context) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context);
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
backgroundColor: Colors.blue,
centerTitle: true,
title: Text(
'Keywords - $_userRole', // Display role dynamically in the AppBar
style: TextStyle(color: Colors.white),
),
),
drawer: buildDrawer(context, orgSlug: widget.orgSlug),
body: organizationViewModel.isLoading
? Center(child: CircularProgressIndicator())
: Stack(
children: [
buildBaseScreen(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Keywords',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
_showCreateKeywordDialog(context);
},
child: Text('Create New Keyword'),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: 'Search by Project Name or URL',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
_filterKeywords(value); // Call filter method on text change
},
),
),
Expanded(
child: _filteredKeywords.isEmpty
? Center(
child: Text('No keywords found for this organization'),
)
: ListView.builder(
itemCount: _filteredKeywords.length,
itemBuilder: (context, index) {
final keyword = _filteredKeywords[index];
return Card(
margin: const EdgeInsets.symmetric(vertical: 8.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.grey[300]!),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Displaying ID
Text(
'Id: ${keyword.id.toString()}',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
// Project Name
Text(
'Project Name: ${keyword.projectName ?? ''}',
style: TextStyle(fontSize: 14),
),
SizedBox(height: 8),
// URL
GestureDetector(
onTap: () {
// Handle URL click if needed
},
child: Text(
'URL: ${keyword.url ?? ''}',
style: TextStyle(color: Colors.blue, decoration: TextDecoration.underline),
),
),
SizedBox(height: 8),
// Displaying Keywords as tags
Wrap(
spacing: 6.0,
runSpacing: 6.0,
children: keyword.keyword != null
? keyword.keyword!.map((kw) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(20),
),
child: Text(
kw,
style: TextStyle(color: Colors.white),
),
);
}).toList()
: [],
),
SizedBox(height: 8),
// Row for edit and delete icons
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Icon(Icons.edit, color: Colors.blue),
onPressed: () {
_showEditKeywordDialog(context, keyword);
},
tooltip: 'Edit Keyword',
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
_showDeleteKeywordDialog(context, keyword);
},
tooltip: 'Delete Keyword',
),
],
),
],
),
),
);
},
),
),
],
),
),
),
],
),
);
}
void _showCreateKeywordDialog(BuildContext context) async {
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
await showDialog(
context: context,
builder: (BuildContext context) {
return CreateKeywordDialog(
orgSlug: widget.orgSlug,
orgRoleId: orgRoleId!,
orgUserId: orgUserId!,
orgUserorgId: orgUserorgId!,
);
},
);
}
void _showEditKeywordDialog(BuildContext context, dynamic keyword) async {
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
await showDialog(
context: context,
builder: (BuildContext context) {
return CreateKeywordDialog(
orgSlug: widget.orgSlug,
orgRoleId: orgRoleId!,
orgUserId: orgUserId!,
orgUserorgId: orgUserorgId!,
keywordId: keyword.id.toString(), // Pass the keyword ID for editing
existingKeywords: keyword.keyword, // Pass existing keywords for editing
selectedProjectId: keyword.projectId.toString(), // Pass the selected project ID
selectedUrlname: keyword.url, // Pass the selected URL ID
selectedUrlId: keyword.urlId.toString(), // Pass the selected URL ID
);
},
);
}
void _showDeleteKeywordDialog(BuildContext context, keyword) async {
final confirmed = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Delete Keyword'),
content: Text('Are you sure you want to delete this keyword?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(false); // Cancel action
},
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(true); // Confirm action
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: Text('Delete'),
),
],
);
},
);
if (confirmed == true) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
await organizationViewModel.deleteKeyword(keyword.id.toString()); // Assuming deleteUrl method takes an ID
_fetchData(); // Refresh the data after deletion
}
}
Widget buildBaseScreen({required Widget body}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: body,
);
}
}
Search button type filter
Initialization of Member Data Map
Map<String, List<WebRatings>> memberDataMap = {};
This line creates a map named memberDataMap where the keys are strings representing team member names (e.g., "Alexa Global") and the values are lists of WebRatings. This map will hold data specific to each team member for later retrieval.
Definition of Team Members:
final List<String> teamMembers = [
"Alexa Global",
"Alexa USA",
"Domain",
"Alexa India",
"Backlinks",
"Referring Domain"
];
or
final List<Map<String, dynamic>> buttons = [
{'label': 'Subscriber', 'color': Colors.red},
{'label': 'Facebook', 'color': Colors.purple},
{'label': 'Followers', 'color': Colors.teal},
{'label': 'Instagram', 'color': Colors.blue},
];
Data Fetching Method:
Future<void> _fetchData() async {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final credentials = await _getCredentials();
final email = credentials['email'];
final orgSlug = widget.orgSlug;
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
setState(() {
_userRole = determineUserRole(orgRoleId); // Use the mixin to determine the role
});
if (email != null && orgSlug.isNotEmpty) {
await organizationViewModel.getWebRating(email, orgSlug, orgRoleId.toString(), orgUserId.toString(), orgUserorgId.toString());
_filteredWebData = organizationViewModel.my_website; // Initialize with full data
_populateMemberDataMap();// look here
}
}
Populating Member Data Map:
void _populateMemberDataMap() {
// Clear the existing data
memberDataMap.clear();
// Loop through the defined team members
for (var member in teamMembers) {
// Filter web data based on the project names that match the member
memberDataMap[member] = _filteredWebData.where((data) {
// Adjust this logic based on your requirements
return data.projectName != null && data.projectName!.contains(member);
}).toList();
}
}
Building Dynamic Buttons:
_buildDynamicButtons(), // Dynamic buttons for team members
Building Dynamic Buttons function defination:
Widget _buildDynamicButtons() {
_populateMemberDataMap(); // Call this to ensure the map is populated before displaying buttons
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: teamMembers.map((memberName) {
return GestureDetector(
onTap: () {
_showDetailsDialog(memberName); // Call to show details dialog
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
margin: EdgeInsets.only(right: 8), // Spacing between buttons
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(20),
),
child: Text(
memberName,
style: TextStyle(color: Colors.white),
),
),
);
}).toList(),
),
);
}
Showing Details Dialog:
void _showDetailsDialog(String memberName) {
// Filter the data based on the selected member name
List<WebRatings> filteredData = _filteredWebData.where((data) {
// Check if the memberName matches any relevant fields in the web data
switch (memberName) {
case "Alexa Global":
return data.globalRank != null; // Ensure that global rank is not null
case "Alexa USA":
return data.usaRank != null; // Ensure that USA rank is not null
case "Domain":
return data.domain != null; // Ensure that domain is not null
case "Alexa India":
return data.indiaRank != null; // Ensure that India rank is not null
case "Backlinks":
return data.backlinks != null; // Ensure that backlinks are not null
case "Referring Domain":
return data.refer != null; // Ensure that referring domains are not null
default:
return false; // No matching case
}
}).toList();
// Create a dialog showing details for the selected member
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('$memberName Details'),
content: Container(
width: double.maxFinite,
height: 400, // Adjust the height based on your needs
child: filteredData.isNotEmpty
? ListView.builder(
itemCount: filteredData.length,
itemBuilder: (context, index) {
final data = filteredData[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Project Name: ${data.projectName ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Domain Authority: ${data.domain ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Alexa Global: ${data.globalRank ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Alexa USA: ${data.usaRank ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Alexa India: ${data.indiaRank ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Backlinks: ${data.backlinks ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Referring Domains: ${data.refer ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Divider(), // Divider between entries for better readability
],
),
);
},
)
: Center(child: Text('No data available for $memberName.')),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
child: Text('Close'),
),
],
);
},
);
}
output
Full code implemention
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:provider/provider.dart';
import 'package:wizbrand/Screen/Influencer/createweb.dart';
import 'package:wizbrand/Screen/layout/drawer.dart';
import 'package:wizbrand/Screen/layout/rolemixins.dart';
import 'package:wizbrand/model/webrating.dart';
import 'package:wizbrand/view_modal/organization_view_model.dart';
class WebScreen extends StatefulWidget {
final String orgSlug;
WebScreen({required this.orgSlug});
@override
_WebScreenState createState() => _WebScreenState();
}
class _WebScreenState extends State<WebScreen> with DrawerMixin, RoleMixin {
String _userRole = ''; // Variable to store the role for AppBar
TextEditingController _searchController = TextEditingController();
List<WebRatings> _filteredWebData = []; // List to hold filtered web data
Map<String, List<WebRatings>> memberDataMap = {};
@override
void initState() {
super.initState();
_fetchData(); // Fetch organization data when the screen initializes
}
final List<String> teamMembers = [
"Alexa Global",
"Alexa USA",
"Domain",
"Alexa India",
"Backlinks",
"Referring Domain"
];
Future<void> _fetchData() async {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final credentials = await _getCredentials();
final email = credentials['email'];
final orgSlug = widget.orgSlug;
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
setState(() {
_userRole = determineUserRole(orgRoleId); // Use the mixin to determine the role
});
if (email != null && orgSlug.isNotEmpty) {
await organizationViewModel.getWebRating(email, orgSlug, orgRoleId.toString(), orgUserId.toString(), orgUserorgId.toString());
_filteredWebData = organizationViewModel.my_website; // Initialize with full data
_populateMemberDataMap();
}
}
void _populateMemberDataMap() {
// Clear the existing data
memberDataMap.clear();
// Loop through the defined team members
for (var member in teamMembers) {
// Filter web data based on the project names that match the member
memberDataMap[member] = _filteredWebData.where((data) {
// Adjust this logic based on your requirements
return data.projectName != null && data.projectName!.contains(member);
}).toList();
}
}
void _filterWebData(String query) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
final allWebData = organizationViewModel.my_website;
setState(() {
_filteredWebData = allWebData.where((webData) {
final projectNameLower = webData.projectName?.toLowerCase() ?? '';
final urlLower = webData.domain?.toLowerCase() ?? ''; // Adjust as needed for the URL field
final queryLower = query.toLowerCase();
return projectNameLower.contains(queryLower) ||
urlLower.contains(queryLower);
}).toList();
});
}
Future<Map<String, String?>> _getCredentials() async {
final secureStorage = FlutterSecureStorage();
String? email = await secureStorage.read(key: 'email');
String? password = await secureStorage.read(key: 'password');
return {'email': email, 'password': password};
}
@override
Widget build(BuildContext context) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context);
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
backgroundColor: Colors.blue,
centerTitle: true,
title: Text(
'Web Rankings - $_userRole', // Display role dynamically in the AppBar
style: TextStyle(color: Colors.white),
),
),
drawer: buildDrawer(context, orgSlug: widget.orgSlug),
body: organizationViewModel.isLoading
? Center(child: CircularProgressIndicator()) // Display loading indicator while fetching data
: Stack(
children: [
buildBaseScreen(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Web Rankings',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _searchController,
decoration: InputDecoration(
labelText: 'Search by Project Name or Domain',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
onChanged: (value) {
_filterWebData(value); // Call filter method on text change
},
),
),
ElevatedButton(
onPressed: () {
_showCreateWebRankingDialog(context);
},
child: Text('Create New Web Ranking'),
),
SizedBox(height: 20),
_buildDynamicButtons(), // Dynamic buttons for team members
Expanded(
child: _filteredWebData.isEmpty // Check filtered web data
? Center(child: Text('No Web ranking found'))
: ListView.builder(
itemCount: _filteredWebData.length,
itemBuilder: (context, index) {
final webdata = _filteredWebData[index];
return Card(
margin: const EdgeInsets.symmetric(vertical: 8.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
side: BorderSide(color: Colors.grey[300]!),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ID
Text(
'ID: ${webdata.id ?? 'N/A'}',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
// Date
Text(
'Date: ${webdata.createdAt ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
SizedBox(height: 8),
// Project Name
Text(
'Project Name: ${webdata.projectName ?? 'No Project Name'}',
style: TextStyle(fontSize: 14),
),
SizedBox(height: 8),
// Displaying Web Ranking details using Wrap to avoid overflow
Wrap(
spacing: 6.0,
runSpacing: 6.0,
children: [
_buildTag('Domain Authority: ${webdata.domain ?? 'N/A'}'),
_buildTag('Alexa Global: ${webdata.globalRank ?? 'N/A'}'),
_buildTag('Alexa USA: ${webdata.usaRank ?? 'N/A'}'),
_buildTag('Alexa India: ${webdata.indiaRank ?? 'N/A'}'),
_buildTag('Backlinks: ${webdata.backlinks ?? 'N/A'}'),
_buildTag('Referring Domains: ${webdata.refer ?? 'N/A'}'),
],
),
SizedBox(height: 8),
// Row for edit and delete icons
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Icon(Icons.edit, color: Colors.blue),
onPressed: () {
_showEditWebRankingDialog(context, webdata);
},
tooltip: 'Edit Web Ranking',
),
IconButton(
icon: Icon(Icons.delete, color: Colors.red),
onPressed: () {
_showDeleteWebRankingDialog(context, webdata);
},
tooltip: 'Delete Web Ranking',
),
],
),
],
),
),
);
},
),
),
],
),
),
),
],
),
);
}
// Helper method to build tags
Widget _buildTag(String text) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(20),
),
child: Text(
text,
style: TextStyle(color: Colors.white),
),
);
}
// Method to build the dynamic buttons below the search bar
Widget _buildDynamicButtons() {
_populateMemberDataMap(); // Call this to ensure the map is populated before displaying buttons
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: teamMembers.map((memberName) {
return GestureDetector(
onTap: () {
_showDetailsDialog(memberName); // Call to show details dialog
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
margin: EdgeInsets.only(right: 8), // Spacing between buttons
decoration: BoxDecoration(
color: Colors.purple,
borderRadius: BorderRadius.circular(20),
),
child: Text(
memberName,
style: TextStyle(color: Colors.white),
),
),
);
}).toList(),
),
);
}
// Method to show details dialog for a selected project
void _showDetailsDialog(String memberName) {
// Filter the data based on the selected member name
List<WebRatings> filteredData = _filteredWebData.where((data) {
// Check if the memberName matches any relevant fields in the web data
switch (memberName) {
case "Alexa Global":
return data.globalRank != null; // Ensure that global rank is not null
case "Alexa USA":
return data.usaRank != null; // Ensure that USA rank is not null
case "Domain":
return data.domain != null; // Ensure that domain is not null
case "Alexa India":
return data.indiaRank != null; // Ensure that India rank is not null
case "Backlinks":
return data.backlinks != null; // Ensure that backlinks are not null
case "Referring Domain":
return data.refer != null; // Ensure that referring domains are not null
default:
return false; // No matching case
}
}).toList();
// Create a dialog showing details for the selected member
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('$memberName Details'),
content: Container(
width: double.maxFinite,
height: 400, // Adjust the height based on your needs
child: filteredData.isNotEmpty
? ListView.builder(
itemCount: filteredData.length,
itemBuilder: (context, index) {
final data = filteredData[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Project Name: ${data.projectName ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Domain Authority: ${data.domain ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Alexa Global: ${data.globalRank ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Alexa USA: ${data.usaRank ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Alexa India: ${data.indiaRank ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Backlinks: ${data.backlinks ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Text(
'Referring Domains: ${data.refer ?? 'N/A'}',
style: TextStyle(fontSize: 14),
),
Divider(), // Divider between entries for better readability
],
),
);
},
)
: Center(child: Text('No data available for $memberName.')),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
child: Text('Close'),
),
],
);
},
);
}
// Method to show details dialog for a selected project
// Method to display a dialog for creating a Web Ranking entry
void _showCreateWebRankingDialog(BuildContext context) async {
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
await showDialog(
context: context,
builder: (BuildContext context) {
return CreateWebDialog(
orgSlug: widget.orgSlug,
orgRoleId: orgRoleId!,
orgUserId: orgUserId!,
orgUserorgId: orgUserorgId!,
); // Passing the orgSlug
},
);
}
void _showEditWebRankingDialog(BuildContext context, dynamic webData) async {
final secureStorage = FlutterSecureStorage();
final orgRoleId = await secureStorage.read(key: 'orgRoleId');
final orgUserId = await secureStorage.read(key: 'orgUserId');
final orgUserorgId = await secureStorage.read(key: 'orgUserorgId');
await showDialog(
context: context,
builder: (BuildContext context) {
return CreateWebDialog(
orgSlug: widget.orgSlug,
orgRoleId: orgRoleId!,
orgUserId: orgUserId!,
orgUserorgId: orgUserorgId!,
webid: webData.id.toString(),
projectId: webData.projectId.toString(),
projectName: webData.projectName,
domain: webData.domain,
globalRank: webData.globalRank?.toString(),
usaRank: webData.usaRank?.toString(),
indiaRank: webData.indiaRank?.toString(),
backlinks: webData.backlinks?.toString(),
referringDomains: webData.refer?.toString(),
);
},
);
}
// Method to display a dialog for deleting a Web Ranking entry
void _showDeleteWebRankingDialog(BuildContext context, dynamic webdata) async {
final confirmed = await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Delete Web Ranking'),
content: Text('Are you sure you want to delete this web ranking entry?'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(false); // Cancel action
},
child: Text('Cancel'),
),
ElevatedButton(
onPressed: () {
Navigator.of(context).pop(true); // Confirm action
},
style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
child: Text('Delete'),
),
],
);
},
);
if (confirmed == true) {
final organizationViewModel = Provider.of<OrganizationViewModel>(context, listen: false);
await organizationViewModel.deleteWeb(webdata.id.toString()); // Assuming deleteCompetitor method takes an ID
_fetchData(); // Refresh the data after deletion
}
}
Widget buildBaseScreen({required Widget body}) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
),
child: body,
);
}
}
Search button with different button color type filter
To add dynamic labels and colors to your buttons, you can modify your current code to incorporate a list of maps containing button properties such as label and color. Here's how you can integrate this into your existing setup:
- Define the buttons list: We'll use a list of maps to define the labels and colors for your buttons.
final List<Map<String, dynamic>> buttons = [
{'label': 'Alexa Global', 'color': Colors.red},
{'label': 'Alexa USA', 'color': Colors.purple},
{'label': 'Domain', 'color': Colors.teal},
{'label': 'Alexa India', 'color': Colors.blue},
{'label': 'Backlinks', 'color': Colors.orange},
{'label': 'Referring Domain', 'color': Colors.green},
];
- Modify the _buildDynamicButtons function: Instead of mapping through teamMembers, we will loop through the buttons list and use the label and color values for each button.
Widget _buildDynamicButtons() {
_populateMemberDataMap(); // Ensure the map is populated before displaying buttons
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: buttons.map((button) {
return GestureDetector(
onTap: () {
_showDetailsDialog(button['label']); // Use the label to show details
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
margin: EdgeInsets.only(right: 8), // Spacing between buttons
decoration: BoxDecoration(
color: button['color'], // Set the button color dynamically
borderRadius: BorderRadius.circular(20),
),
child: Text(
button['label'], // Display the button label
style: TextStyle(color: Colors.white),
),
),
);
}).toList(),
),
);
}
- Update _showDetailsDialog to handle the dynamic labels: Make sure the dialog shows the correct data for the dynamic buttons. You already have this logic in place based on the memberName.
void _showDetailsDialog(String memberName) {
List<WebRatings> filteredData = _filteredWebData.where((data) {
switch (memberName) {
case "Alexa Global":
return data.globalRank != null;
case "Alexa USA":
return data.usaRank != null;
case "Domain":
return data.domain != null;
case "Alexa India":
return data.indiaRank != null;
case "Backlinks":
return data.backlinks != null;
case "Referring Domain":
return data.refer != null;
default:
return false;
}
}).toList();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('$memberName Details'),
content: Container(
width: double.maxFinite,
height: 400, // Adjust the height as needed
child: filteredData.isNotEmpty
? ListView.builder(
itemCount: filteredData.length,
itemBuilder: (context, index) {
final data = filteredData[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Project Name: ${data.projectName ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Domain Authority: ${data.domain ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Alexa Global: ${data.globalRank ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Alexa USA: ${data.usaRank ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Alexa India: ${data.indiaRank ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Backlinks: ${data.backlinks ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Referring Domains: ${data.refer ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Divider(),
],
),
);
},
)
: Center(child: Text('No data available for $memberName.')),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
child: Text('Close'),
),
],
);
},
);
}
Full Code Implementation:
Here is the complete code, integrating the list of dynamic buttons with labels and colors:
// List of dynamic buttons with labels and colors
final List<Map<String, dynamic>> buttons = [
{'label': 'Alexa Global', 'color': Colors.red},
{'label': 'Alexa USA', 'color': Colors.purple},
{'label': 'Domain', 'color': Colors.teal},
{'label': 'Alexa India', 'color': Colors.blue},
{'label': 'Backlinks', 'color': Colors.orange},
{'label': 'Referring Domain', 'color': Colors.green},
];
// Building Dynamic Buttons
Widget _buildDynamicButtons() {
_populateMemberDataMap(); // Ensure the map is populated before displaying buttons
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: buttons.map((button) {
return GestureDetector(
onTap: () {
_showDetailsDialog(button['label']); // Use the label to show details
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
margin: EdgeInsets.only(right: 8), // Spacing between buttons
decoration: BoxDecoration(
color: button['color'], // Set the button color dynamically
borderRadius: BorderRadius.circular(20),
),
child: Text(
button['label'], // Display the button label
style: TextStyle(color: Colors.white),
),
),
);
}).toList(),
),
);
}
// Populating Member Data Map
void _populateMemberDataMap() {
memberDataMap.clear();
for (var member in teamMembers) {
memberDataMap[member] = _filteredWebData.where((data) {
return data.projectName != null && data.projectName!.contains(member);
}).toList();
}
}
// Showing Details Dialog
void _showDetailsDialog(String memberName) {
List<WebRatings> filteredData = _filteredWebData.where((data) {
switch (memberName) {
case "Alexa Global":
return data.globalRank != null;
case "Alexa USA":
return data.usaRank != null;
case "Domain":
return data.domain != null;
case "Alexa India":
return data.indiaRank != null;
case "Backlinks":
return data.backlinks != null;
case "Referring Domain":
return data.refer != null;
default:
return false;
}
}).toList();
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('$memberName Details'),
content: Container(
width: double.maxFinite,
height: 400,
child: filteredData.isNotEmpty
? ListView.builder(
itemCount: filteredData.length,
itemBuilder: (context, index) {
final data = filteredData[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Project Name: ${data.projectName ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Domain Authority: ${data.domain ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Alexa Global: ${data.globalRank ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Alexa USA: ${data.usaRank ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Alexa India: ${data.indiaRank ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Backlinks: ${data.backlinks ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Text('Referring Domains: ${data.refer ?? 'N/A'}', style: TextStyle(fontSize: 14)),
Divider(),
],
),
);
},
)
: Center(child: Text('No data available for $memberName.')),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
child: Text('Close'),
),
],
);
},
);
}
This approach allows you to create dynamic buttons with different labels and colors, while the logic for showing filtered data remains intact.
Top comments (0)