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.
InfiniteMathPlot #
A Flutter package for plotting interactive mathematical graphs. You give it an equation string, it draws the curve. Zoom in, zoom out, pan around — it just works.
I built this because every graphing widget I found either required you to pre-compute points yourself, didn't handle discontinuities properly, or fell apart when you zoomed in too far. This one handles all of that internally.
What it can do #
- Plot Cartesian, implicit, parametric, and polar equations from a plain string
- Handle discontinuities automatically —
tan(x),1/x,sqrt(x)all render correctly without any extra configuration - Zoom and pan with pinch gestures, scroll wheel, or drag
- Plot multiple curves on the same graph, each with its own color
- Draw a grid and labeled axes that scale with the viewport
- Adapt to any screen size — works on mobile, tablet, desktop, and web
Installation #
Add this to your pubspec.yaml:
dependencies:
infinite_math_plot: ^0.1.1
Run flutter pub get, then import it:
import 'package:infinite_math_plot/infinite_math_plot.dart';
Getting started #
The simplest usage is just dropping the widget somewhere and giving it an equation:
InfiniteMathPlot(
equation: 'y = sin(x)',
)
That gives you a full-screen interactive graph with a grid, axes, and a blue sine curve. You can already zoom and pan at this point.
If you want to control the initial view:
InfiniteMathPlot(
equation: 'y = x^2',
xMin: -5,
xMax: 5,
yMin: -1,
yMax: 10,
)
Equation types #
Cartesian #
The standard y = f(x) form. Just write the right-hand side:
InfiniteMathPlot(equation: 'y = sin(x)')
InfiniteMathPlot(equation: 'y = x^3 - 2*x + 1')
InfiniteMathPlot(equation: 'y = 1/x') // asymptote at x=0, handled automatically
InfiniteMathPlot(equation: 'y = sqrt(x)') // undefined for x < 0, handled automatically
InfiniteMathPlot(equation: 'y = abs(x - 2)')
Implicit #
Equations with both x and y. The package finds the zero-level set using marching squares:
InfiniteMathPlot(equation: 'x^2 + y^2 = 25') // circle
InfiniteMathPlot(equation: 'x^2/9 + y^2/4 = 1') // ellipse
InfiniteMathPlot(equation: 'y^2 = x^3 - x') // elliptic curve
Parametric #
Both x and y as functions of t, separated by a comma. t runs from 0 to 2π:
InfiniteMathPlot(equation: 'x = cos(t), y = sin(t)') // circle
InfiniteMathPlot(equation: 'x = sin(3*t), y = sin(2*t)') // Lissajous figure
InfiniteMathPlot(equation: 'x = t*cos(t), y = t*sin(t)') // spiral
Polar #
r as a function of the angle. Use theta or θ:
InfiniteMathPlot(equation: 'r = cos(3*theta)') // 3-petal rose
InfiniteMathPlot(equation: 'r = 1 + cos(theta)') // cardioid
InfiniteMathPlot(equation: 'r = theta') // Archimedean spiral
Plotting multiple curves #
Use equations instead of equation and pass a list of EquationConfig objects:
InfiniteMathPlot(
equations: [
EquationConfig(equation: 'y = sin(x)', color: Colors.blue),
EquationConfig(equation: 'y = cos(x)', color: Colors.orange),
EquationConfig(equation: 'y = sin(x) * cos(x)', color: Colors.green, strokeWidth: 1.5),
],
)
You can't use both equation and equations at the same time — the widget will throw an assertion error if you try.
Customization #
Turning off grid or axes #
InfiniteMathPlot(
equation: 'y = x^2',
showGrid: false, // no grid lines
showAxes: false, // no axes or tick labels
)
Disabling gestures #
// Static graph, no interaction
InfiniteMathPlot(
equation: 'y = sin(x)',
enableZoom: false,
enablePan: false,
)
Changing the curve style #
InfiniteMathPlot(
equation: 'y = sin(x)',
graphColor: Colors.deepPurple,
strokeWidth: 3.0,
)
Sampling precision #
The default is 2 sample points per logical pixel, which looks good for most equations. If you're plotting something with rapid oscillations (like y = sin(50*x)), bump it up:
InfiniteMathPlot(
equation: 'y = sin(50*x)',
samplingPrecision: 4.0,
)
For simple equations where performance matters more than smoothness, you can lower it:
InfiniteMathPlot(
equation: 'y = x',
samplingPrecision: 1.0,
)
All parameters #
InfiniteMathPlot #
| Parameter | Type | Default | What it does |
|---|---|---|---|
equation |
String? |
— | Single equation. Use this or equations, not both. |
equations |
List<EquationConfig>? |
— | Multiple equations. Use this or equation, not both. |
xMin |
double |
-10 |
Left edge of the initial viewport. |
xMax |
double |
10 |
Right edge of the initial viewport. |
yMin |
double |
-10 |
Bottom edge of the initial viewport. |
yMax |
double |
10 |
Top edge of the initial viewport. |
showGrid |
bool |
true |
Whether to draw grid lines. |
showAxes |
bool |
true |
Whether to draw axes and tick labels. |
enableZoom |
bool |
true |
Whether pinch and scroll-wheel zoom is active. |
enablePan |
bool |
true |
Whether drag-to-pan is active. |
graphColor |
Color |
Colors.blue |
Curve color (single-equation mode only). |
strokeWidth |
double |
2.0 |
Curve thickness in logical pixels (single-equation mode only). |
samplingPrecision |
double |
2.0 |
Sample points per logical pixel. Higher = smoother. |
EquationConfig #
| Field | Type | Default | What it does |
|---|---|---|---|
equation |
String |
required | The equation string. |
color |
Color |
required | Curve color. |
strokeWidth |
double |
2.0 |
Curve thickness in logical pixels. |
Supported math #
Functions #
sin, cos, tan, asin, acos, atan, sqrt, abs, exp, log, pow
'y = sin(x)'
'y = pow(x, 3)' // same as x^3
'y = exp(x)' // same as e^x
'y = log(x)' // natural log
Constants #
pi, π, e
'y = sin(pi * x)'
'y = e^x'
Operators #
+, -, *, /, ^ (exponentiation)
A few things to watch out for:
- Use
^for powers, not** - Always write
*explicitly —2xwon't parse,2*xwill - For polar equations, use
thetaorθas the angle variable
A note on discontinuities #
The package distinguishes between two situations that look similar but are actually different:
True asymptotes — like 1/x near x=0 or tan(x) near π/2. The function jumps from a large positive value to a large negative value (or vice versa) as it crosses through the viewport. The package detects this and breaks the path there, so you don't get a spurious vertical line connecting the two branches.
Steep but continuous functions — like y = x³ when you're zoomed out. The y-values can be enormous (x=10 gives y=1000), but the function is perfectly continuous. The package does not break the path here, even though the jump between consecutive samples is large. It correctly identifies that both values are on the same side of the viewport and keeps the curve connected.
This distinction matters because a naive "break the path if the jump is too large" approach would shred x³ into fragments when zoomed out. The package avoids that by requiring that a suspected asymptote has values straddling the viewport — one above yMax and one below yMin — before breaking the path.
Running the example app #
There's a demo app in the example/ folder:
cd example
flutter run
It has 8 preset equations you can switch between, a text field where you can type any equation and plot it live, and toggles for grid, axes, zoom, and pan. Good for quickly testing what the package can do.
Running the tests #
flutter test
There are 241 tests covering the parser, coordinate transformer, discontinuity detector, sample generator, painter, gesture handler, and the top-level widget. The test suite includes property-based round-trip tests (parse → serialize → parse, screen → math → screen) in addition to regular unit and widget tests.
Common issues #
Gaps in the curve where there shouldn't be any
This usually means the equation has a domain restriction the package is respecting. sqrt(x) is undefined for negative x, so the left half of the graph will be empty. log(x) is undefined for x ≤ 0. This is correct behavior, not a bug.
If you're seeing gaps in a function that should be continuous everywhere, check whether samplingPrecision is high enough. Very rapidly oscillating functions need more samples to render correctly.
Parse error on a valid-looking equation
A few syntax rules to check:
- Multiplication must be explicit: write
2*x, not2x - Use
^for exponentiation:x^2, notx**2 - Parametric equations need a comma between the two parts:
x = cos(t), y = sin(t) - For polar, the angle variable is
thetaorθ, nott
Hot reload fails with "Const class cannot become non-const"
This is a Flutter hot reload limitation. The GestureHandler class holds mutable zoom state (_lastScale) so it can't be const. Just do a hot restart (R in the terminal, or the restart button in your IDE) instead of hot reload. Hot restart picks up all changes cleanly.
License #
MIT