A powerful Dart code generation library that automatically generates HTTP client code, Flutter table widgets, and pagination-aware fetch methods from simple annotations.
- 🚀 HTTP Client Generation: Automatically generate HTTP client implementations from annotated interfaces
- 📊 Table Widget Generation: Create Flutter table widgets with automatic header and data cell generation
- 📄 Pagination Support: Generate pagination-aware fetch methods for list endpoints
- 🔧 Flexible Annotations: Customize HTTP methods, URLs, field labels, and more
- 🌐 Multi-Client Support: Support for both HTTP/1.1 and HTTP/2 clients
- 🎯 Type Safety: Full type checking and compile-time safety
Add component_generator to your pubspec.yaml:
dependencies:
component_generator: ^0.1.1
dev_dependencies:
build_runner: ^2.3.2- Create your HTTP client:
import 'package:component_generator/http1_client.dart';
class MyClient extends HttpClientBase {
MyClient() : super();
@override
int checkResult(RespData<dynamic> resp, String url, Map<String, String> headers) {
return 0; // Your response validation logic
}
}
MyClient client = MyClient();- Define your data models:
class LoginParams extends JSONParameter {
String loginName;
String password;
int pageNum = 0;
int pageSize = 0;
LoginParams(this.loginName, this.password);
@override
Map<String, dynamic> toJson() => {
"loginName": loginName,
"password": password,
"pageNum": pageNum,
"pageSize": pageSize,
};
}
class UserInfo {
int id;
String name;
String email;
UserInfo({this.id = 0, this.name = "", this.email = ""});
factory UserInfo.fromJson(Map<String, dynamic> json) => UserInfo(
id: json['id'] ?? 0,
name: json['name'] ?? "",
email: json['email'] ?? "",
);
}
class UserListResp {
int total;
List<UserInfo> list;
UserListResp({this.total = 0, this.list = const []});
factory UserListResp.fromJson(Map<String, dynamic> json) => UserListResp(
total: json['total'] ?? 0,
list: (json['list'] as List? ?? []).map((e) => UserInfo.fromJson(e)).toList(),
);
}- Create your network interface:
import 'package:component_generator/component_generator.dart';
part 'network.g.dart';
@DataInterface()
abstract class Network {
@ReqConfig("/user/login")
Future<RespData> login(LoginParams data);
@ReqConfig("/user/list")
Future<RespData<UserListResp?>> listUser(LoginParams data);
}- Generate code:
dart run build_runner build- Use the generated client:
void main() async {
client.prefix = "https://api.example.com";
// Login
final loginResult = await networkApi.login(LoginParams("user", "pass"));
print(loginResult.obj);
// List users
final userList = await networkApi.listUser(LoginParams("", ""));
print("Total users: ${userList.obj?.total}");
}Marks an abstract class as a network interface that will generate HTTP client implementations.
@DataInterface(
name: "customService", // Optional: Custom service instance name
client: "myClient", // Optional: Custom client variable name
mixins: "LoggingMixin" // Optional: Mixins for generated implementation
)
abstract class MyApi {
// Define your HTTP methods here
}Generated Output:
class MyApiImpl extends BaseMethod implements MyApi {
MyApiImpl({super.client});
// Implementation of all abstract methods
}
var customService = MyApiImpl(client: myClient);Defines an HTTP endpoint for a method in a @DataInterface class.
@DataInterface()
abstract class UserApi {
@ReqConfig("/users", method: "GET")
Future<RespData<List<User>>> getUsers();
@ReqConfig("/users", method: "POST")
Future<RespData<User>> createUser(CreateUserParams data);
@ReqConfig("/users/:id", method: "PUT")
Future<RespData<User>> updateUser(int id, UpdateUserParams data);
@ReqConfig("/users/:id", method: "DELETE")
Future<RespData<void>> deleteUser(int id);
}Parameters:
url(required): The API endpoint URLmethod(optional): HTTP method ("GET", "POST", "PUT", "DELETE"). Default: "POST"keyType(optional): Type for buffer key. Default: "int"
Generates Flutter table widgets with automatic header and data cell generation. Used on mixin classes that extend TableContentWidget.
import 'package:flutter/material.dart';
import 'package:component_generator/component_generator.dart';
part 'user_table.g.dart';
@TableWidget(
fetchClass: UserApi, // Required: The fetch class type
fetchMethod: "listUser", // Required: The fetch method name
useI18n: false, // Optional: Enable i18n (default: false)
i18nFunction: 'tr', // Optional: i18n function name (default: 'tr')
columns: [], // Optional: Custom column order
skips: [] // Optional: Fields to skip
)
mixin UserTableMixin on TableContentWidget<UserInfo, UserReq> {
// Optional: Override specific cells by field name
DataCell genIdDataCell(BuildContext context, UserInfo item) {
return DataCell(
IconButton(
icon: const Icon(Icons.edit),
onPressed: () => _editUser(context, item),
),
);
}
// Optional: Override tap handlers by field name
void onNameTap(BuildContext context, UserInfo item) {
// Handle name cell tap
print('Tapped on user: ${item.name}');
}
}Generated Output:
class UserTable extends TableContentWidget<UserInfo, UserReq> with UserTableMixin {
const UserTable({super.key});
@override
Future<List<UserInfo>> fetchData(PaginationController<UserReq> controller) {
return UserApiFetch.listUser(controller); // Auto-generated if not implemented in mixin
}
@override
List<DataColumn> genTableHeader(BuildContext context) {
return [
DataColumn(label: const Text('ID')),
DataColumn(label: const Text('Name')),
DataColumn(label: const Text('Email')),
];
}
@override
List<DataCell> genTableData(BuildContext context, UserInfo item) {
return [
DataCell(Text(item.id.toString()), onTap: () => onIdTap(context, item)),
DataCell(Text(item.name), onTap: () => onNameTap(context, item)),
DataCell(Text(item.email)),
];
}
}The @TableWidget annotation supports predefined methods for customizing cell behavior:
genFieldNameDataCell(BuildContext context, T item)- Override cell for specific field
onFieldNameTap(BuildContext context, T item)- Handle tap on cells for specific field
These methods are automatically detected and used by the generator if implemented in your mixin.
The @DataInterface annotation automatically generates pagination-aware fetch methods for list endpoints.
@DataInterface schema code can be generated by gos.
@DataInterface()
abstract class ProductApi {
@ReqConfig("/products")
Future<RespData<ProductListResp?>> listProducts(ProductReq data);
}
// Automatically generates (part file, same library): an implementation class
// named `${InterfaceName}Api` (e.g. `NetworkApi` for `Network`) plus an instance,
// and list helpers such as:
//
// Future<List<ProductInfo>> listProductsFetch(
// IPaginationController<ProductReq> controller) async { ... }Requirements:
- Response type must have
listandtotalfields - Request type must extend
JSONParameterand includepageNumandpageSizefields - Generated fetch methods use the type name
IPaginationController<T>(not prefixed). Your library file that declarespart '...g.dart'must import a package that defines that interface (same name and members), so the part can resolve the type—for examplepackage:component_set/paging/pagination_controller.dartas inexample/lib/network.dart, or your own abstraction.
class ProductListPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PaginatedView<ProductReq>(
initialParam: ProductReq(),
child: const ProductTableImpl(), // Use the generated Impl class
);
}
}class MyClient extends HttpClientBase {
MyClient() : super();
@override
int checkResult(RespData<dynamic> resp, String url, Map<String, String> headers) {
// Custom response validation
if (resp.code == 401) {
// Handle unauthorized
return -1;
}
return 0; // Success
}
@override
Future<Map<String, String>> getHeaders(String url) async {
// Add custom headers
return {
'Authorization': 'Bearer ${await getToken()}',
'Content-Type': 'application/json',
};
}
}The package includes pre-configured builders, so no additional build.yaml configuration is required. The builders are automatically enabled when you add the package to your dependencies.
See the /example directory for a complete working example that demonstrates:
- HTTP client setup
- Data models with JSON serialization
- Network interface definition
- Code generation
- Flutter table widget usage
- Type Safety: Always use strongly typed response objects
- Error Handling: Implement proper error handling in your HTTP client
- Naming Conventions: Use consistent naming for services and methods
- Pagination: Include
pageNumandpageSizefields for list endpoints - Code Generation: Run
dart run build_runner buildafter changing annotations
- "Undefined class" errors: Make sure all imports are correct
- "Method not found": Run code generation after adding new methods
- Build failures: Ensure all required dependencies are installed
- Use
dart run build_runner build --verbosefor detailed build output - Check generated
.g.dartfiles for issues - Ensure all required fields are present in your data models
Contributions are welcome! Please feel free to submit a Pull Request.
This package is licensed under the MIT License. See the LICENSE file for details.