infinite_math_plot 0.1.1
infinite_math_plot: ^0.1.1 copied to clipboard
A Flutter package that renders interactive mathematical graphs on a simulated infinite coordinate plane.
import 'package:flutter/material.dart';
import 'package:infinite_math_plot/infinite_math_plot.dart';
void main() => runApp(const DemoApp());
class DemoApp extends StatelessWidget {
const DemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'InfiniteMathPlot Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
home: const DemoHome(),
);
}
}
// ---------------------------------------------------------------------------
// Preset definitions
// ---------------------------------------------------------------------------
class _Preset {
final String label;
final List<EquationConfig> equations;
const _Preset({required this.label, required this.equations});
}
final _presets = [
_Preset(
label: 'Sine & Cosine',
equations: [
const EquationConfig(equation: 'y = sin(x)', color: Colors.blue, strokeWidth: 2),
const EquationConfig(equation: 'y = cos(x)', color: Colors.orange, strokeWidth: 2),
],
),
_Preset(
label: 'Parabola',
equations: [
const EquationConfig(equation: 'y = x^2', color: Colors.green, strokeWidth: 2.5),
],
),
_Preset(
label: 'Tangent (discontinuous)',
equations: [
const EquationConfig(equation: 'y = tan(x)', color: Colors.red, strokeWidth: 2),
],
),
_Preset(
label: '1/x (asymptote)',
equations: [
const EquationConfig(equation: 'y = 1/x', color: Colors.purple, strokeWidth: 2),
],
),
_Preset(
label: 'Circle (implicit)',
equations: [
const EquationConfig(equation: 'x^2 + y^2 = 25', color: Colors.teal, strokeWidth: 2),
],
),
_Preset(
label: 'Lissajous (parametric)',
equations: [
const EquationConfig(
equation: 'x = sin(3*t), y = sin(2*t)',
color: Colors.deepOrange,
strokeWidth: 2,
),
],
),
_Preset(
label: 'Rose (polar)',
equations: [
const EquationConfig(equation: 'r = cos(3*theta)', color: Colors.pink, strokeWidth: 2),
],
),
_Preset(
label: 'Multi-curve',
equations: [
const EquationConfig(equation: 'y = x', color: Colors.blue, strokeWidth: 1.5),
const EquationConfig(equation: 'y = x^2', color: Colors.green, strokeWidth: 1.5),
const EquationConfig(equation: 'y = x^3', color: Colors.red, strokeWidth: 1.5),
const EquationConfig(equation: 'y = sin(x)', color: Colors.orange, strokeWidth: 1.5),
const EquationConfig(equation: 'y = cos(x)', color: Colors.purple, strokeWidth: 1.5),
],
),
];
// ---------------------------------------------------------------------------
// Home screen
// ---------------------------------------------------------------------------
class DemoHome extends StatefulWidget {
const DemoHome({super.key});
@override
State<DemoHome> createState() => _DemoHomeState();
}
class _DemoHomeState extends State<DemoHome> {
int _presetIndex = 0;
bool _showGrid = true;
bool _showAxes = true;
bool _enableZoom = true;
bool _enablePan = true;
// Custom equation input
final _controller = TextEditingController(text: 'y = sin(x)');
String? _customEquation;
String? _parseError;
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _applyCustom() {
final input = _controller.text.trim();
if (input.isEmpty) return;
try {
// Validate by parsing
EquationParser().parse(input);
setState(() {
_customEquation = input;
_parseError = null;
_presetIndex = -1; // custom mode
});
} on ParseException catch (e) {
setState(() => _parseError = e.message);
}
}
List<EquationConfig> get _activeEquations {
if (_presetIndex >= 0) return _presets[_presetIndex].equations;
return [
EquationConfig(
equation: _customEquation!,
color: Colors.indigo,
strokeWidth: 2.5,
),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('InfiniteMathPlot Demo'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: Column(
children: [
// ---- Graph ----
Expanded(
child: InfiniteMathPlot(
equations: _activeEquations,
showGrid: _showGrid,
showAxes: _showAxes,
enableZoom: _enableZoom,
enablePan: _enablePan,
),
),
// ---- Controls ----
Container(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
padding: const EdgeInsets.fromLTRB(12, 8, 12, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Preset chips
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
for (var i = 0; i < _presets.length; i++)
Padding(
padding: const EdgeInsets.only(right: 6),
child: ChoiceChip(
label: Text(_presets[i].label),
selected: _presetIndex == i,
onSelected: (_) =>
setState(() {
_presetIndex = i;
_customEquation = null;
_parseError = null;
}),
),
),
],
),
),
const SizedBox(height: 8),
// Custom equation input
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Custom equation',
hintText: 'e.g. y = sin(x) + cos(2*x)',
errorText: _parseError,
isDense: true,
border: const OutlineInputBorder(),
),
onSubmitted: (_) => _applyCustom(),
),
),
const SizedBox(width: 8),
FilledButton(
onPressed: _applyCustom,
child: const Text('Plot'),
),
],
),
const SizedBox(height: 8),
// Toggle row
Wrap(
spacing: 4,
children: [
FilterChip(
label: const Text('Grid'),
selected: _showGrid,
onSelected: (v) => setState(() => _showGrid = v),
),
FilterChip(
label: const Text('Axes'),
selected: _showAxes,
onSelected: (v) => setState(() => _showAxes = v),
),
FilterChip(
label: const Text('Zoom'),
selected: _enableZoom,
onSelected: (v) => setState(() => _enableZoom = v),
),
FilterChip(
label: const Text('Pan'),
selected: _enablePan,
onSelected: (v) => setState(() => _enablePan = v),
),
],
),
],
),
),
],
),
);
}
}