Controllers

A controller is a Dart class that extends Controller and groups related request handlers. Each handler method returns Future<String> — the HTTP response body.

Creating a Controller

import 'package:finch/finch_route.dart';

class UserController extends Controller {
  UserController();

  @override
  Future<String> index() async {
    return rq.renderData(data: {'message': 'User list'});
  }

  Future<String> show() async {
    var id = rq.getParam('id');
    return rq.renderData(data: {'id': id});
  }

  Future<String> profile() async {
    rq.addParam('title', 'User Profile');
    return rq.renderView(path: 'user/profile');
  }
}

The rq Object

Inside any Controller subclass, rq is a getter that returns Context.rq — the current Request object. It gives access to request data, session, cookies, and all response methods.

Attaching a Controller to a Route

final userController = UserController();

FinchRoute(
  key: 'users.index',
  path: 'users',
  methods: Methods.ONLY_GET,
  controller: userController,  // calls userController.index()
),
FinchRoute(
  key: 'users.show',
  path: 'users/{id}',
  methods: Methods.ONLY_GET,
  index: userController.show,  // calls a specific method
),

controller uses the index() method. index accepts a Future<String> Function()? — pass a method reference from the controller to call a different action.

Response Methods

All response methods are on the rq object:

Method Description
rq.renderView(path: 'template/path') Render a Jinja template from widgetsPath
rq.renderData(data: {...}) JSON response
rq.renderString(text: '...') Plain text response
rq.renderHtml(html: '...') HTML string response
rq.renderTag(tag: Tag) Render an Htmler Tag
rq.renderError(404) Standard error response
rq.redirect('/path') HTTP redirect
rq.renderSSE(stream) Server-Sent Events stream
rq.renderSocket() WebSocket handshake

renderView

Future<String> dashboard() async {
  rq.addParams({
    'title': 'Dashboard',
    'user': currentUser,
  });
  return rq.renderView(path: 'pages/dashboard');
  // Loads: widgetsPath/pages/dashboard.{widgetsType}
}

renderData (JSON)

Future<String> apiUsers() async {
  var users = await UserCollection().getAll();
  return rq.renderData(data: {'users': users.map((u) => u.toJson()).toList()});
}

renderError

Future<String> notFound() async {
  return rq.renderError(404);
}

Reading Request Data

// Read a GET or POST field
var name = rq.get<String>('name', def: 'anonymous');
var age  = rq.get<int>('age', def: 0);

// Read a path parameter from {id}
var id = rq.getParam('id');

// Check request method
if (rq.isPost) { ... }
if (rq.method == Methods.DELETE) { ... }

// API endpoint? (path starts with /api/)
bool isApi = rq.isApiEndpoint;

Passing Data to Templates

Future<String> show() async {
  var user = await UserCollection().findById(rq.getParam('id'));

  // addParam / addParams makes data available in Jinja templates
  rq.addParams({
    'user': user?.toJson(),
    'title': 'User Profile',
  });

  return rq.renderView(path: 'user/show');
}

In the template:

<h1>{{ user.name }}</h1>

Accessing Other Controllers

Controllers are plain Dart objects. Instantiate them at the top level and share:

final authController = AppAuthController();
final homeController = HomeController();

// In AuthController.loginPost():
return homeController.renderView('example/form');