liquify 1.0.0-dev.2 
liquify: ^1.0.0-dev.2 copied to clipboard
A powerful and extensible Liquid template engine for Dart. Supports full Liquid syntax, custom tags and filters, and high-performance parsing and rendering.
Liquify - Liquid Template Engine for Dart #
Liquify is a comprehensive Dart implementation of the Liquid template language, originally created by Shopify. This high-performance library allows you to parse, render, and extend Liquid templates in your Dart and Flutter applications.
Features #
- Full support for standard Liquid syntax and semantics
 - Synchronous and asynchronous rendering
 - Extensible architecture for custom tags and filters (both sync and async)
 - High-performance parsing and rendering
 - Strong typing and null safety
 - Comprehensive error handling and reporting
 - Support for complex data structures and nested objects
 - Easy integration with Dart and Flutter projects
 - Extensive set of built-in filters ported from LiquidJS
 - File system abstraction for template resolution
 
Installation #
Add Liquify to your package's pubspec.yaml file:
dependencies:
  liquify: ^0.8.1
Or, for the latest development version:
dependencies:
  liquify:
    git: https://github.com/kingwill101/liquify.git
Then run dart pub get or flutter pub get to install the package.
Usage #
For detailed usage examples, please refer to the example directory in the repository. Here are some basic usage scenarios:
Basic Template Rendering #
Templates can be rendered both synchronously and asynchronously:
import 'package:liquify/liquify.dart';
void main() async {
  final data = {
    'name': 'Alice',
    'items': ['apple', 'banana', 'cherry']
  };
  final template = Template.parse(
    'Hello, {{ name | upcase }}! Your items are: {% for item in items %}{{ item }}{% unless forloop.last %}, {% endunless %}{% endfor %}.',
    data: data
  );
  
  // Synchronous rendering
  final syncResult = template.render();
  print(syncResult);
  // Output: Hello, ALICE! Your items are: apple, banana, cherry.
  // Asynchronous rendering (useful for async filters or includes)
  final asyncResult = await template.renderAsync();
  print(asyncResult);
  // Output: Hello, ALICE! Your items are: apple, banana, cherry.
}
You can also update the template context between renders:
final template = Template.parse('{{ greeting }} {{ name }}!');
// Update context
template.updateContext({
  'greeting': 'Hello',
  'name': 'World'
});
print(await template.renderAsync()); // "Hello World!"
// Update context again
template.updateContext({'greeting': 'Goodbye'});
print(await template.renderAsync()); // "Goodbye World!"
File System and Template Resolution #
Liquify provides flexible ways to resolve and load templates from various sources. The Root class is the base for implementing template resolution strategies.
Using MapRoot for In-Memory Templates
MapRoot is a simple implementation of Root that stores templates in memory:
import 'package:liquify/liquify.dart';
void main() {
  final fs = MapRoot({
    'resume.liquid': '''
Name: {{ name }}
Skills: {{ skills | join: ", " }}
{% render 'greeting.liquid' with name: name, greeting: "Welcome" %}
''',
    'greeting.liquid': '{{ greeting }}, {{ name }}!',
  });
  final context = {
    'name': 'Alice Johnson',
    'skills': ['Dart', 'Flutter', 'Liquid'],
  };
  final template = Template.fromFile('resume.liquid', fs, data: context);
  print(template.render());
}
Custom Template Resolution
For more complex scenarios, such as loading templates from a file system or a database, you can create a custom subclass of Root:
class FileSystemRoot extends Root {
  final String basePath;
  FileSystemRoot(this.basePath);
  
  @override
  String? resolve(String path) {
    final file = File('$basePath/$path');
    if (file.existsSync()) {
      return file.readAsStringSync();
    }
    return null;
  }
}
void main() async {
  final fs = FileSystemRoot('/path/to/templates');
  final template = Template.fromFile('resume.liquid', fs, data: context);
  
  // Sync rendering
  print(template.render());
  
  // Async rendering (recommended for templates with includes or async filters)
  print(await template.renderAsync());
}
// Example with async template resolution
class AsyncFileSystemRoot extends Root {
  final String basePath;
  AsyncFileSystemRoot(this.basePath);
  
  @override
  Future<String?> resolveAsync(String path) async {
    final file = File('$basePath/$path');
    if (await file.exists()) {
      return await file.readAsString();
    }
    return null;
  }
}
The async support is particularly useful when:
- Loading templates from remote sources
 - Using async filters or tags
 - Processing large templates with many includes
 - Implementing database-backed template storage
 
This approach allows you to implement custom logic for resolving and loading templates from any source, such as a file system, database, or network resource.
The render tag uses this resolution mechanism to include and render other templates, allowing for modular and reusable template structures.
Custom Tags and Filters #
Liquify allows you to create custom tags and filters. For detailed examples, please refer to the example directory in the repository.
API Documentation #
Detailed API documentation is available here.
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License #
This project is licensed under the MIT License.
Acknowledgements #
- Shopify for the original Liquid template language
 - The Dart team for the excellent language and tools
 - LiquidJS for their comprehensive set of filters, which we've ported to Dart
 - liquid_dart for their initial Dart implementation, which served as inspiration for this project
 
Related Projects #
- LiquidJS: A popular JavaScript implementation of Liquid templates
 - liquid_dart: An earlier Dart implementation of Liquid templates (now unmaintained)
 
Liquify aims to provide a modern, maintained, and feature-rich Liquid template engine for the Dart ecosystem, building upon the work of these excellent projects.