artifact 1.0.14 copy "artifact: ^1.0.14" to clipboard
artifact: ^1.0.14 copied to clipboard

Data Modeling for the local madman

Map objects to maps or json without part files or any nonsense! Just an annotation!

flutter pub add artifact
flutter pub add artifact_gen --dev

Make the model class

@artifact // Artifact is all you need!
class Animal {
  @rename("hp")
  final int health;
  String? someNonSerializableField;
  
  // Parameters define what is mapped
  // Default values are used if not set
  // Required parameters will throw if not set
  const Animal({this.health = 100});
}

Let's add two subclasses for animals

@artifact
class Dog extends Animal {
  final bool goodBoy;

  const Dog({@rename("hp") super.health = 125, this.goodBoy = true});
}
@artifact
class Cat extends Animal {
  final int lives;
  
  const Cat({@rename("hp") super.health = 70, this.lives = 9});
}

Lets put this in a world object

@artifact
class World {
  final List<Animal> animals;
  
  const World({this.animals = const []});
}

Then use it!

World world = World(
  animals: [
    Dog(goodBoy: true),
    Cat(lives: 8, health: 50),
  ]
);

print(jsonEncode(world.toMap()));
{
  "animals": [
    {
      "_subclass_Animal": "Dog",
      "hp": 125,
      "goodBoy": true
    },
    {
      "_subclass_Animal": "Cat",
      "hp": 50,
      "lives": 8
    }
  ]
}

CopyWith also exists since artifacts are immutable

You deserialize with the generated extension

Animal animal = $Animal.fromMap(...)
    // Add 10 hp
    .copyWith(deltaHp: 10);

if(animal is Cat) {
  // Add a life
  (animal as Cat).copyWith(deltaLives: 1);
}

Attachments #

@attach("Any const object")
@artifact
class SomeModel {
    @attach(42)
    final String name;
    
    @attach(const SomeClassData())
    final int age;
    
    const SomeModel({
        this.name = "John Doe",
        this.age = 0,
    });
}

Then you can get via attachments

List<dynamic> all = $SomeModel.rootAttachments;

// Or get field by attachment
SomeModel m = SomeModel(name: "Dan", age: 2);

String n = m.getAttachment<int, String>(42);
// n = "Dan

Custom Attachments #

You can simply extend the attachment class to make it simpler to work with and utilize

// First we define a data class or enum to signal information
enum UiComponentType {
  title,
  subtitle,
}

// Then we extend annotations on attachment
class UITitle extends attachment{
  const UITitle(super.data = UiComponentType.title);
}

class UISubtitle extends attachment{
  const UIComponent(super.data = UiComponentType.subtitle);
}

// These are optional if you want to skip the ()
const uititle = UITitle();
const uisubtitle = UISubtitle();

Then we can use it in our models as annotations

@artifact
class Person {
  @uititle
  final String name;
  
  @uisubtitle
  final String email;
  
  const Person({required this.name, required this.email});
}

Now, we can use it!

List<User> users = ...

Widget build(BuildContext context) => ListView(children: [
  ...users.map((i) => ListTile(
    title: Text(i.getAttachment(UiComponentType.title)),
    subtitle: Text(i.getAttachment(UiComponentType.subtitle)),
  ))
]);
0
likes
130
points
1.39k
downloads

Publisher

verified publisherarcane.art

Weekly Downloads

Data Modeling for the local madman

Repository (GitHub)

Documentation

API reference

License

GPL-3.0 (license)

Dependencies

fast_log, toxic

More

Packages that depend on artifact