view_matrix 1.2.0
view_matrix: ^1.2.0 copied to clipboard
A flexible and extensible Flutter framework for rendering list and grid views with unified API and local data handling.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:view_matrix/view_matrix.dart';
void main() => runApp(const ExampleApp());
class ExampleApp extends StatelessWidget {
const ExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "ViewMatrix Example",
theme: ThemeData(
colorSchemeSeed: Colors.indigo,
useMaterial3: true,
),
home: const ExampleHome(),
);
}
}
class ExampleHome extends StatelessWidget {
const ExampleHome({super.key});
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 7,
child: Scaffold(
backgroundColor: Colors.grey.shade100,
appBar: AppBar(
elevation: 1,
shadowColor: Colors.transparent,
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.white,
title: const Text(
"ViewMatrix Examples",
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 21,
color: Colors.indigo,
),
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48),
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
isScrollable: true,
// -------- TEXT --------
labelColor: Colors.indigo,
unselectedLabelColor: Colors.black54,
labelStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
),
unselectedLabelStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
),
// -------- SPACING --------
labelPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
// -------- PROPER UNDERLINE INDICATOR --------
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(
color: Colors.indigo,
width: 3,
),
insets: EdgeInsets.symmetric(horizontal: 16),
),
indicatorSize: TabBarIndicatorSize.label,
tabs: const [
Tab(text: "API - GET"),
Tab(text: "API - POST"),
Tab(text: "Local Grid"),
Tab(text: "Fully Custom UI"),
Tab(text: "Custom Builder List"),
Tab(text: "Custom Builder Grid"),
Tab(text: "Minimal Grid"),
],
),
),
),
),
body: const TabBarView(
children: [
ApiGetExample(),
ApiPostExample(),
LocalGridExample(),
FullCustomExample(),
CustomItemBuilderExample(),
CustomItemBuilderGridExample(),
CustomItemBuilderGridMinimalExample(),
],
),
),
);
}
}
//
// -------------------------------------------------------------
// 1️⃣ API GET EXAMPLE
// -------------------------------------------------------------
class ApiGetExample extends StatelessWidget {
const ApiGetExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.list,
sourceType: SourceType.api,
apiConfig: ApiConfig(
url: "https://fakestoreapi.com/products",
method: HttpMethod.get,
enableLogging: true,
showOnly: [
DataItem.fieldImage,
DataItem.fieldTitle,
DataItem.fieldDescription,
DataItem.fieldBadge,
],
fieldMapping: {
DataItem.fieldTitle: "title",
DataItem.fieldDescription: "description",
DataItem.fieldImage: "image",
DataItem.fieldBadge: "category"
},
),
uiConfig: ViewMatrixUIConfig(
titleStyle: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
color: Colors.indigo,
),
descriptionStyle: TextStyle(
fontSize: 13,
color: Colors.grey.shade700,
),
badgeStyle: const TextStyle(
color: Colors.indigo,
fontSize: 12,
fontWeight: FontWeight.w600,
),
badgeBackgroundColor: Colors.indigo.shade50,
cardRadius: 16,
cardElevation: 2,
imageHeight: 150,
loaderColor: Colors.indigo,
),
);
}
}
//
// -------------------------------------------------------------
// 2️⃣ API POST
// -------------------------------------------------------------
class ApiPostExample extends StatelessWidget {
const ApiPostExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.list,
sourceType: SourceType.api,
apiConfig: ApiConfig(
url: "https://jsonplaceholder.typicode.com/posts",
method: HttpMethod.post,
enableLogging: true,
body: {
"title": "ViewMatrix User",
"body": "Flutter Developer",
"userId": 1
},
successStatusCodes: [200, 201, 202, 204],
showOnly: [
DataItem.fieldTitle,
DataItem.fieldDescription,
DataItem.fieldBadge
],
fieldMapping: {
DataItem.fieldTitle: "title",
DataItem.fieldDescription: "body",
DataItem.fieldBadge: "userId"
},
),
uiConfig: ViewMatrixUIConfig(
titleStyle: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
color: Colors.green,
),
badgeBackgroundColor: Colors.green.shade50,
badgeStyle: const TextStyle(
color: Colors.green,
fontWeight: FontWeight.w500,
),
loaderColor: Colors.green,
cardRadius: 16,
),
);
}
}
//
// -------------------------------------------------------------
// 3️⃣ Local Grid
// -------------------------------------------------------------
class LocalGridExample extends StatelessWidget {
const LocalGridExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.grid,
sourceType: SourceType.nonApi,
localItems: [
DataItem(title: "Laptop", description: "Macbook Pro", badge: "Tech"),
DataItem(title: "Bike", description: "Yamaha R15", badge: "Vehicles"),
DataItem(title: "Shoes", description: "Nike Air", badge: "Fashion"),
DataItem(title: "Phone", description: "iPhone 15 Pro", badge: "Tech"),
DataItem(title: "Watch", description: "Apple Watch", badge: "Tech"),
DataItem(title: "Camera", description: "Sony A7", badge: "Tech"),
],
uiConfig: ViewMatrixUIConfig(
gridCrossAxisCount: 3,
gridSpacing: 16,
cardRadius: 18,
imageHeight: 110,
badgeBackgroundColor: Colors.orange.shade50,
badgeStyle: const TextStyle(
color: Colors.deepOrange,
fontWeight: FontWeight.bold,
),
),
);
}
}
//
// -------------------------------------------------------------
// 4️⃣ Custom Dark Grid
// -------------------------------------------------------------
class FullCustomExample extends StatelessWidget {
const FullCustomExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.grid,
sourceType: SourceType.api,
apiConfig: ApiConfig(
url: "https://fakestoreapi.com/products",
method: HttpMethod.get,
enableLogging: false,
fieldMapping: {
DataItem.fieldTitle: "title",
DataItem.fieldDescription: "description",
DataItem.fieldImage: "image",
},
),
uiConfig: ViewMatrixUIConfig(
gridCrossAxisCount: 2,
customGridBuilder: (item) {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: BorderRadius.circular(18),
),
child: Column(
children: [
Expanded(
child: ClipRRect(
borderRadius:
const BorderRadius.vertical(top: Radius.circular(18)),
child: Image.network(
item.image ?? "",
fit: BoxFit.cover,
width: double.infinity,
),
),
),
Padding(
padding: const EdgeInsets.all(12),
child: Text(
item.title,
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
},
),
);
}
}
//
// -------------------------------------------------------------
// 5️⃣ List Gradient
// -------------------------------------------------------------
class CustomItemBuilderExample extends StatelessWidget {
const CustomItemBuilderExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.list,
sourceType: SourceType.api,
apiConfig: ApiConfig(
url: "https://fakestoreapi.com/products",
method: HttpMethod.get,
fieldMapping: {
DataItem.fieldTitle: "title",
DataItem.fieldDescription: "description",
DataItem.fieldImage: "image",
DataItem.fieldBadge: "category",
},
),
itemBuilder: (context, item, index) {
return Card(
elevation: 5,
margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.purple.shade400, Colors.blue.shade400],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
),
padding: const EdgeInsets.all(16),
child: Row(
children: [
ClipRRect(
borderRadius: BorderRadius.circular(14),
child: Image.network(
item.image ?? "",
width: 80,
height: 80,
fit: BoxFit.cover,
),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.title,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
if (item.description != null)
Text(
item.description!,
style: const TextStyle(
color: Colors.white70, fontSize: 13),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
if (item.badge != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(12),
),
child: Text(
item.badge!,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600),
),
),
],
),
),
],
),
),
);
},
);
}
}
//
// -------------------------------------------------------------
// 6️⃣ Grid Gradient
// -------------------------------------------------------------
class CustomItemBuilderGridExample extends StatelessWidget {
const CustomItemBuilderGridExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.grid,
sourceType: SourceType.api,
apiConfig: ApiConfig(
url: "https://fakestoreapi.com/products",
method: HttpMethod.get,
fieldMapping: {
DataItem.fieldTitle: "title",
DataItem.fieldDescription: "description",
DataItem.fieldImage: "image",
DataItem.fieldBadge: "category",
},
),
itemBuilder: (context, item, index) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
colors: [Colors.indigo.shade400, Colors.deepPurple.shade600],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Column(
children: [
Expanded(
flex: 6,
child: Image.network(
item.image ?? "",
width: double.infinity,
fit: BoxFit.cover,
),
),
Expanded(
flex: 4,
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (item.badge != null)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(8),
),
child: Text(
item.badge!,
style: const TextStyle(
color: Colors.white, fontSize: 11),
),
),
const SizedBox(height: 6),
Expanded(
child: Text(
item.title,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 14,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
if (item.description != null)
Text(
item.description!,
style: const TextStyle(
color: Colors.white70, fontSize: 10),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
),
),
)
],
),
),
);
},
uiConfig: ViewMatrixUIConfig(
gridCrossAxisCount: 2,
gridSpacing: 14,
gridChildAspectRatio: 0.76,
),
);
}
}
//
// -------------------------------------------------------------
// 7️⃣ Minimal Grid
// -------------------------------------------------------------
class CustomItemBuilderGridMinimalExample extends StatelessWidget {
const CustomItemBuilderGridMinimalExample({super.key});
@override
Widget build(BuildContext context) {
return ViewMatrix(
viewType: ViewType.grid,
sourceType: SourceType.api,
apiConfig: ApiConfig(
url: "https://fakestoreapi.com/products",
method: HttpMethod.get,
fieldMapping: {
DataItem.fieldTitle: "title",
DataItem.fieldImage: "image",
DataItem.fieldBadge: "category",
},
),
itemBuilder: (context, item, index) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(20),
blurRadius: 10,
)
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Column(
children: [
Expanded(
flex: 7,
child: Stack(
children: [
Positioned.fill(
child: Image.network(
item.image ?? "",
fit: BoxFit.cover,
),
),
if (item.badge != null)
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
),
child: Text(
item.badge!,
style: const TextStyle(
fontSize: 10, fontWeight: FontWeight.bold),
),
),
),
],
),
),
Expanded(
flex: 3,
child: Padding(
padding: const EdgeInsets.all(10),
child: Text(
item.title,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
),
)
],
),
),
);
},
uiConfig: ViewMatrixUIConfig(
gridCrossAxisCount: 2,
gridSpacing: 18,
gridChildAspectRatio: 0.8,
),
);
}
}