import 'package:flutter/material.dart';
import 'package:runanywhere/runanywhere.dart';
class ToolCallingScreen extends StatefulWidget {
const ToolCallingScreen({super.key});
@override
State<ToolCallingScreen> createState() => _ToolCallingScreenState();
}
class _ToolCallingScreenState extends State<ToolCallingScreen> {
String _response = '';
List<String> _toolLog = [];
bool _isLoading = false;
@override
void initState() {
super.initState();
_registerTools();
}
void _registerTools() {
RunAnywhereTools.clearTools();
RunAnywhereTools.registerTool(
const ToolDefinition(
name: 'get_weather',
description: 'Gets the current weather for a location',
parameters: [
ToolParameter(
name: 'location',
type: ToolParameterType.string,
description: 'City name, e.g. "San Francisco"',
),
],
category: 'Utility',
),
_fetchWeather,
);
RunAnywhereTools.registerTool(
const ToolDefinition(
name: 'calculate',
description: 'Performs basic arithmetic on two numbers',
parameters: [
ToolParameter(
name: 'a',
type: ToolParameterType.number,
description: 'First operand',
),
ToolParameter(
name: 'b',
type: ToolParameterType.number,
description: 'Second operand',
),
ToolParameter(
name: 'operation',
type: ToolParameterType.string,
description: 'One of: add, subtract, multiply, divide',
),
],
category: 'Math',
),
_calculate,
);
RunAnywhereTools.registerTool(
const ToolDefinition(
name: 'get_current_time',
description: 'Returns the current date and time',
parameters: [
ToolParameter(
name: 'timezone',
type: ToolParameterType.string,
description: 'IANA timezone, e.g. "America/New_York"',
),
],
category: 'Utility',
),
_getTime,
);
}
Future<Map<String, ToolValue>> _fetchWeather(
Map<String, ToolValue> args,
) async {
final location = args['location']?.stringValue ?? 'Unknown';
return {
'temperature': NumberToolValue(72.0),
'condition': StringToolValue('sunny'),
'location': StringToolValue(location),
};
}
Future<Map<String, ToolValue>> _calculate(
Map<String, ToolValue> args,
) async {
final a = (args['a'] as NumberToolValue?)?.value ?? 0;
final b = (args['b'] as NumberToolValue?)?.value ?? 0;
final op = args['operation']?.stringValue ?? 'add';
final result = switch (op) {
'add' => a + b,
'subtract' => a - b,
'multiply' => a * b,
'divide' => b != 0 ? a / b : double.nan,
_ => double.nan,
};
return {'result': NumberToolValue(result)};
}
Future<Map<String, ToolValue>> _getTime(
Map<String, ToolValue> args,
) async {
final timezone = args['timezone']?.stringValue ?? 'UTC';
final now = DateTime.now().toUtc();
return {
'time': StringToolValue(now.toIso8601String()),
'timezone': StringToolValue(timezone),
};
}
Future<void> _runPrompt(String prompt) async {
setState(() {
_isLoading = true;
_response = '';
_toolLog = [];
});
try {
final result = await RunAnywhereTools.generateWithTools(
prompt,
options: const ToolCallingOptions(
maxToolCalls: 3,
autoExecute: true,
temperature: 0.7,
maxTokens: 512,
),
);
setState(() {
_response = result.text;
_toolLog = [
for (final call in result.toolCalls)
'→ ${call.toolName}(${call.arguments.map(
(k, v) => MapEntry(k, v.stringValue),
)})',
for (final res in result.toolResults)
res.success
? '✓ ${res.result}'
: '✗ ${res.error}',
];
});
} catch (e) {
setState(() => _response = 'Error: $e');
} finally {
setState(() => _isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Tool Calling Demo')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Wrap(
spacing: 8,
runSpacing: 8,
children: [
ElevatedButton(
onPressed: _isLoading
? null
: () => _runPrompt(
'What is the weather in San Francisco?',
),
child: const Text('Weather'),
),
ElevatedButton(
onPressed: _isLoading
? null
: () => _runPrompt('What is 42 * 17?'),
child: const Text('Calculate'),
),
ElevatedButton(
onPressed: _isLoading
? null
: () => _runPrompt('What time is it in UTC?'),
child: const Text('Time'),
),
],
),
const SizedBox(height: 16),
if (_isLoading) const LinearProgressIndicator(),
const SizedBox(height: 16),
if (_toolLog.isNotEmpty) ...[
Text(
'Tool Activity',
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 4),
for (final log in _toolLog)
Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Text(log, style: const TextStyle(fontFamily: 'monospace', fontSize: 13)),
),
const Divider(height: 24),
],
if (_response.isNotEmpty)
Expanded(
child: SingleChildScrollView(
child: Text(_response, style: const TextStyle(fontSize: 16)),
),
),
],
),
),
);
}
}