kind 0.6.0 kind: ^0.6.0 copied to clipboard
Powerful JSON serialization, database-object mapping, test data generation, and more. Describe your data model with Kind<T> subclasses (such as IntKind, ListKind<T>, etc.).
Introduction #
This is "package:kind", a Dart package for creating static objects that describe "what kind of data" you have.
Licensed under the Apache License 2.0.
Links #
Key features #
- Flexible
- The package makes you think many things through, such as:
- How many bits an integer needs? 32? 52? 64?
- What is the maximum length of a list?
- When you convert
DateTime
instances to/from JSON, should you use UTC or local time zone?
- Once you have specified your data model, the package lets you do:
- JSON serialization
- Database object mapping
- Automatic implementation of
==
,hashCode
, andtoString()
. - And more!
- The package makes you think many things through, such as:
- No code generation.
- You write
Kind<T>
specifications manually (see examples below). However, AI code assistants can easily automate the writing. - The package also works easily for any class. You don't need to modify the classes themselves. You could, for example, serialize Flutter SDK widgets.
- You write
Sounds interesting? We encourage you to compare this framework to older, more established packages such as json_serializable and pick your favorite.
Built-in kinds #
- Fixed-length primitives
- Variable-length primitives
- Collection classes:
- Polymorphic values:
- User-defined classes:
- CompositeKind
- ImmutableKind
- Three possible constructors:
Things you can do #
- Find classes
- Construct instances
- Validate data:
- Describe data:
- JSON serialization
- Reflection:
Getting started #
1.Add dependency #
In terminal, run the following in your project directory:
flutter pub add kind
Does not work? If your project uses Dart SDK rather than Flutter SDK, run dart pub add kind
.
2.Study examples #
See example code below.
Examples #
import 'package:kind/kind.dart';
void main() {
//
// Encode/decode JSON trees:
//
final company = Company.kind.decodeJsonTree({
'name': 'Flutter App Development Experts',
'shareholders': [
{
'@type': 'Person',
'firstName': 'Alice',
'lastName': 'Smith',
},
{
'@type': 'Person',
'firstName': 'Bob',
},
],
});
print("${company.name} has ${company.shareholders.length} shareholders.");
//
// We have `==` and `hashCode` because we extended `HasKind`:
//
print(company.shareholders[1] == Person(firstName: 'Bob')); // --> true
//
// We have `toString()` because we extended `HasKind`:
//
print(company.toString());
// Prints:
// Company(
// "Flutter App Development Experts",
// shareholders: [
// Person(
// firstName: 'John',
// lastName: 'Doe',
// ),
// Person(firstName: 'Bob'),
// ],
// )
}
//
// Example class #1:
// "A static `_walk` function"
//
class Company extends Shareholder {
static const kind = ImmutableKind<Company>(
name: 'Company',
blank: Company(''),
walk: _walk,
);
@override
final String name;
final List<Shareholder> shareholders;
const Company(this.name, {this.shareholders = const []});
@override
Kind<Company> get runtimeKind => kind;
static Company _walk(Mapper f, Company t) {
final name = f.positional(t.name, 'name');
final shareholders = f(
t.shareholders,
'shareholders',
kind: const ListKind(
elementKind: Shareholder.kind,
),
);
// The following is a performance optimization we recommend:
if (f.canReturnSame) {
return t;
}
// Finally, construct a new instance:
return Company(
name,
shareholders: shareholders,
);
}
}
//
// Example #2:
// "A class that extends `Walkable`"
//
class Person extends Shareholder with Walkable {
static const kind = ImmutableKind<Person>.walkable(
name: 'Person',
blank: Person(firstName: ''),
);
/// First name
final String firstName;
/// First name
final String lastName;
const Person({
required this.firstName,
this.lastName = '',
});
@override
String get name => '$firstName $lastName';
@override
Kind<Person> get runtimeKind => kind;
@override
Person walk(Mapper f) {
final firstName = f.required(
this.firstName,
'firstName',
kind: const StringKind.singleLineShort(),
);
final lastName = f(
this.lastName,
'lastName',
kind: const StringKind.singleLineShort(),
);
if (f.canReturnSame) {
return this;
}
return Person(
firstName: firstName,
lastName: lastName,
);
}
}
//
// Example #3:
// "A polymorphic class"
//
abstract class Shareholder extends HasKind {
static const kind = PolymorphicKind<Shareholder>.sealed(
defaultKinds: [
Person.kind,
Company.kind,
],
);
const Shareholder();
String get name;
}