web_query 0.6.0
web_query: ^0.6.0 copied to clipboard
A Flutter package that using simple syntax to parse html and json data.
Web Query #
A powerful Flutter library for querying HTML and JSON data using a simple, intuitive syntax. Extract data from web pages and JSON responses with CSS-like selectors, JSONPath-style navigation, and advanced filtering capabilities.
Table of Contents #
- Features
- Installation
- Quick Start
- Complete Syntax Reference
- Advanced Usage
- API Reference
- Migration Guide
Features #
β¨ Simple & Intuitive Syntax
- CSS-like selectors for HTML elements
- JSONPath-style navigation for JSON data
- Unified query interface for both HTML and JSON
π Powerful Querying
- Element navigation (parent, child, siblings)
- Class name matching with wildcards
- Multiple attribute accessors
- Array indexing and range selection
π§ Data Transformation
- Text transformations (uppercase, lowercase)
- RegExp pattern matching and replacement
- Variable substitution (pageUrl, rootUrl)
- Chainable transforms
π― Advanced Filtering
- Include/exclude filters
- Combined filter conditions
- Support for special characters
π Query Composition
- Fallback queries with
|| - Required queries with
++ - Per-query transformations
Installation #
Add to your pubspec.yaml:
dependencies:
web_query: ^0.5.0
Then run:
flutter pub get
Quick Start #
HTML Query Example #
import 'package:web_query/query.dart';
const html = '''
<div class="article">
<h1>Article Title</h1>
<p class="author">John Doe</p>
<div class="content">
<p>First paragraph</p>
<p>Second paragraph</p>
</div>
</div>
''';
final pageData = PageData("https://example.com", html);
final node = pageData.getRootElement();
// Get article title
final title = QueryString('h1/@text').getValue(node);
// Result: "Article Title"
// Get all paragraphs
final paragraphs = QueryString('*p/@text').getCollectionValue(node);
// Result: ["John Doe", "First paragraph", "Second paragraph"]
// Get content paragraphs only
final content = QueryString('.content/*p/@text').getCollectionValue(node);
// Result: ["First paragraph", "Second paragraph"]
JSON Query Example #
import 'dart:convert';
import 'package:web_query/query.dart';
const jsonData = {
"user": {
"name": "Alice",
"email": "alice@example.com",
"posts": [
{"title": "First Post", "likes": 10},
{"title": "Second Post", "likes": 25}
]
}
};
final pageData = PageData(
"https://api.example.com",
"<html></html>",
jsonData: jsonEncode(jsonData)
);
final node = pageData.getRootElement();
// Get user name
final name = QueryString('json:user/name').getValue(node);
// Result: "Alice"
// Get all post titles
final titles = QueryString('json:user/posts/*/title').getCollectionValue(node);
// Result: ["First Post", "Second Post"]
// Get first post
final firstPost = QueryString('json:user/posts/0/title').getValue(node);
// Result: "First Post"
Complete Syntax Reference #
Query Schemes #
Queries can target HTML, JSON, or URL data:
| Scheme | Description | Example |
|---|---|---|
html: |
Query HTML elements (optional, default) | html:div/p/@text |
json: |
Query JSON data | json:user/name |
url: |
Query and modify URLs | url:host, url:?page=2 |
| (none) | Defaults to HTML | div/p/@text |
HTML Selectors #
Basic Selectors
// CSS selectors
'div' // First div element
'div.classname' // Div with specific class
'#id' // Element by ID
'div > p' // Direct child
'div p' // Descendant
// Selector prefixes
'*p' // ALL paragraphs (querySelectorAll)
'p' // FIRST paragraph (querySelector)
'.content/*div' // All divs in .content
'.content/div' // First div in .content
Complex Selectors
// Attribute selectors
'a[href]' // Links with href attribute
'input[type="text"]' // Text inputs
'div[data-id="123"]' // Custom data attributes
// Pseudo-classes
'li:first-child' // First list item
'tr:nth-child(2)' // Second table row
'p:not(.excluded)' // Paragraphs without class
JSON Path Navigation #
Basic Paths
'json:user/name' // Object property
'json:users/0' // Array index (first item)
'json:users/1' // Array index (second item)
'json:users/*' // All array items
'json:users/0-2' // Array range (items 0, 1, 2)
Nested Paths
'json:data/users/0/profile/name' // Deep nesting
'json:response/items/*/title' // All titles from array
'json:config/settings/api/endpoint' // Multi-level objects
Multiple Paths
// Comma-separated (all required)
'json:user/name,user/email' // Get both name and email
'json:meta/title,meta/description' // Multiple meta fields
// Pipe-separated (optional fallback)
'json:title|meta/title' // Try title, fallback to meta/title
Attribute Accessors #
Attribute accessors extract data from HTML elements:
| Accessor | Description | Example |
|---|---|---|
@ or @text |
Text content | div/@text |
@html or @innerHtml |
Inner HTML | div/@html |
@outerHtml |
Outer HTML | div/@outerHtml |
@href, @src, etc. |
Specific attribute | a/@href, img/@src |
@.classname |
Check class existence | div/@.active β "true" or null |
@.prefix* |
Class with prefix | div/@.btn* β matches "btn-primary" |
@.*suffix |
Class with suffix | div/@.*-lg β matches "btn-lg" |
@.*part* |
Class containing text | div/@.*active* β matches "is-active" |
@attr1|attr2 |
First available attribute | img/@src|data-src |
Class Matching Examples
// Exact class check
'div/@.featured' // Returns "true" if class exists, null otherwise
// Wildcard patterns
'button/@.btn*' // Matches: btn-primary, btn-secondary, btn-large
'div/@.*-active' // Matches: is-active, user-active
'span/@.*icon*' // Matches: icon-home, fa-icon, icon
// Multiple class check (OR logic)
'div/@.primary|.secondary' // Has either primary OR secondary class
Navigation Operators #
Navigate the DOM tree from a selected element:
| Operator | Description | Example |
|---|---|---|
^ |
Parent element | p/^ |
^^ |
Root element | p/^^ |
> |
First child | div/> |
+ |
Next sibling | div/+ |
*+ |
All next siblings | div/*+ |
- |
Previous sibling | div/- |
*- |
All previous siblings | div/*- |
Navigation Examples
// Parent navigation
'.content/p/^' // Parent of paragraph in .content
'.nested/div/^/^' // Grandparent
// Sibling navigation
'h1/+' // Element after h1
'h1/*+' // All elements after h1
'.item/+/+' // Skip one, get next
// Child navigation
'.container/>' // First child of container
'.container/>/p' // First p in first child
Query Parameters #
Parameters modify query behavior and transform results:
// Basic syntax
'selector/@attr?param1=value1¶m2=value2'
// Multiple parameters
'div/@text?transform=upper&filter=important'
Transforms #
Transforms modify the extracted data:
Text Transforms
// Case conversion
'@text?transform=upper' // UPPERCASE
'@text?transform=lower' // lowercase
// Chained transforms
'@text?transform=upper;lower' // Apply multiple
Simplified Regexp Syntax
Use ?regexp= as a shorthand for ?transform=regexp::
// Pattern matching
'@text?regexp=/\d+/' // Extract numbers
// Pattern replacement
'@text?regexp=/old/new/' // Replace text
// Multiple regexp transforms
'@text?regexp=/a/b/®exp=/c/d/' // Chain multiple
RegExp Transforms
// Pattern matching (returns matched text or null)
'@text?transform=regexp:/\d+/' // Extract numbers
'@text?transform=regexp:/[A-Z][a-z]+/' // Extract capitalized words
// Pattern replacement
'@text?transform=regexp:/old/new/' // Replace text
'@href?transform=regexp:/^\/(.+)/${rootUrl}$1/' // Make absolute URL
// With capture groups
'@text?transform=regexp:/(\\d{4})-(\\d{2})-(\\d{2})/$2\\/$3\\/$1/' // Reformat date
// Multiline support (enabled by default)
'div@text?regexp=/^Line 2/Matched/' // Match start of line
'div@text?regexp=/\\ALL/Replaced/' // Match entire content
Special RegExp Keywords
| Keyword | Description | Expands To |
|---|---|---|
\ALL |
Match entire content | ^[\s\S]*$ |
RegExp Variables
| Variable | Description | Example |
|---|---|---|
${pageUrl} |
Full page URL | https://example.com/page |
${rootUrl} |
Origin (scheme + host) | https://example.com |
$1, $2, ... |
Capture groups | From regex pattern |
$0 |
Full match | Entire matched text |
Transform Examples
// URL manipulation
'img/@src?transform=regexp:/^\/(.+)/${rootUrl}$1/'
// "/image.jpg" β "https://example.com/image.jpg"
// Text extraction
'@text?transform=regexp:/Price: \$(\d+\.\d{2})/$1/'
// "Price: $19.99" β "19.99"
// Multiple transforms
'@text?transform=regexp:/\s+/ /;upper;regexp:/HELLO/HI/'
// "hello world" β "HI WORLD"
JSON Transform
Extract JSON data from <script> tags or JavaScript variables:
// Extract from <script type="application/json">
'script#data/@text?transform=json'
// Extract from JavaScript variable
'script/@text?transform=json:config' // var config = {...}
'script/@text?transform=json:window.__DATA__' // window.__DATA__ = {...}
// Chain with JSON query
'script#data/@text?transform=json >> json:items/0/title'
// Wildcard variable matching (supports objects, arrays, and primitives)
'script/@text?transform=json:*Config*' // Matches var myConfig = {...}
'script/@text?transform=json:count' // Matches var count = 42;
Query Piping (>>)
Pass the output of one query as the input to the next:
// Get container -> Get paragraphs -> Get text
'.container >> *p >> @text'
// Get JSON items -> Get tags -> Flatten all tags
'json:items/* >> json:tags/*'
Special Selectors
@keys: Get keys of a JSON object'json:users/@keys' // Returns ["alice", "bob"]
Nested Parsing (HTML in JSON)
Use >> to parse HTML strings embedded in JSON:
// JSON: { "comments": ["<div class='user'>Alice</div>", ...] }
'json:comments/* >> html:.user/@text'
// Result: ["Alice", ...]
Piping vs Transform:
Both can extract JSON from HTML, but they work differently:
// Using transform (parses JSON but returns the whole object)
'script#data/@text?transform=json'
// Result: {"user": "Alice", "id": 123}
// Using pipe (parses JSON AND queries it)
'script#data/@text >> json:user'
// Result: "Alice"
// To make transform equivalent, chain with pipe:
'script#data/@text?transform=json >> json:user'
// Result: "Alice"
When to use >>:
- Processing collections (e.g., list of HTML/JSON strings)
- Switching context between schemes (HTML β JSON)
- Cleaner syntax for multi-step parsing
When to use transform=json:
- Extracting JavaScript variables from script tags (e.g.,
transform=json:*Config*) - Getting the full JSON object without further querying
JavaScript Execution (jseval)
Execute JavaScript code to extract variables from obfuscated or eval-based scripts:
import 'package:web_query/js.dart';
// Configure the JavaScript executor (once at app startup)
configureJsExecutor(FlutterJsExecutor());
// Extract variables from JavaScript
'script/@text?transform=jseval:config'
// Executes the script and extracts the 'config' variable
// Extract multiple variables
'script/@text?transform=jseval:userId,userName,isActive'
// Returns: {"userId": 123, "userName": "Alice", "isActive": true}
// Works with eval() and obfuscated code
'script/@text?transform=jseval:secret'
// Handles: eval('var secret = "hidden_value";')
// Chain with other transforms
'script/@text?transform=jseval:title;upper'
// Extract and uppercase
Requirements:
- Import
package:web_query/js.dart - Call
configureJsExecutor(FlutterJsExecutor())before using jseval - Uses
flutter_jspackage for JavaScript execution
Browser Globals: The JavaScript runtime automatically provides common browser globals:
window- Alias to globalThisdocument- Mock document object with common propertiesconsole- Mock console (log, warn, error, etc.)navigator- Mock navigator with userAgent, language, etc.location- Mock location objectlocalStorage/sessionStorage- Mock storage APIssetTimeout/setInterval- Mock timer functions
Use cases:
- Extracting data from obfuscated JavaScript
- Handling eval()-based variable assignments
- Processing dynamically generated JavaScript code
- Extracting configuration from inline scripts
- Working with scripts that reference window/document objects
Variables and Templates
Save intermediate results and combine them using templates:
// Save variable
'json:title?save=t'
// Use variable in path, regex, or template
'json:items/${id}'
'regexp:/${pattern}/replacement/'
'template:Title: ${t}'
// Combine results (save auto-discards intermediate values)
'json:firstName?save=fn ++ json:lastName?save=ln ++ template:${fn} ${ln}'
// Result: "Alice Smith" (intermediate values auto-discarded)
// Keep intermediate results with &keep
'json:firstName?save=fn&keep ++ json:lastName?save=ln&keep ++ template:${fn} ${ln}'
// Result: ["Alice", "Smith", "Alice Smith"]
// Selective keeping
'json:firstName?save=fn ++ json:lastName?save=ln&keep ++ template:${fn} ${ln}'
// Result: ["Smith", "Alice Smith"] (only lastName and template kept)
Note: When using ?save=, results are automatically omitted from the final output unless you add &keep. This makes templates cleaner by default.
Filters #
Filters remove unwanted results based on content:
Basic Filters
// Include filter (must contain)
'*li/@text?filter=apple' // Only items containing "apple"
// Exclude filter (must NOT contain)
'*li/@text?filter=!banana' // Exclude items with "banana"
// Combined filters (AND logic)
'*li/@text?filter=fruit !bad' // Contains "fruit" AND NOT "bad"
Escaped Characters
// Escaped space (literal space in filter term)
'*li/@text?filter=Date\ Fruit' // Match "Date Fruit"
// Escaped semicolon
'*li/@text?filter=A\;B' // Match "A;B"
// Escaped ampersand
'*li/@text?filter=\&' // Match items with "&"
Filter Examples
// Product filtering
'*div.product/@text?filter=available !sold'
// Products that are available but not sold
// Multi-word matching
'*p/@text?filter=important urgent'
// Paragraphs containing both "important" AND "urgent"
// Complex filtering with transforms
'*a/@href?transform=lower&filter=.pdf !draft'
// PDF links that aren't drafts
Query Chaining #
Combine multiple queries for fallback or required data:
Fallback Queries (||)
// Try first, fallback to second if empty
'json:meta/title||h1/@text'
// Use JSON title if available, otherwise h1 text
// Multiple fallbacks
'json:image||img/@src||@data-image'
// Try JSON, then src attribute, then data-image
Required Queries (++)
// Execute all queries (combine results)
'json:meta/title++json:meta/description'
// Get both title AND description
// Mixed HTML and JSON
'h1/@text++json:meta/keywords++.author/@text'
// Get title, keywords, and author
Combined Chaining
// Fallback with required parts
'json:title||h1/@text++json:description||.content/@text'
// (title OR h1) AND (description OR content)
// Per-query transforms
'json:title?transform=upper||h1/@text?transform=lower'
// Uppercase JSON title, or lowercase h1
Advanced Usage #
Complete Example: Web Scraping #
import 'package:web_query/query.dart';
import 'package:http/http.dart' as http;
Future<void> scrapeArticle(String url) async {
// Fetch HTML
final response = await http.get(Uri.parse(url));
final pageData = PageData(url, response.body);
final node = pageData.getRootElement();
// Extract article data
final title = QueryString('h1.title/@text').getValue(node);
final author = QueryString('.author/@text||.byline/@text').getValue(node);
final date = QueryString('.date/@text?transform=regexp:/(\d{4}-\d{2}-\d{2})/$1/').getValue(node);
final content = QueryString('.article-body/*p/@text').getCollectionValue(node);
final images = QueryString('.article-body/*img/@src?transform=regexp:/^\/(.+)/${rootUrl}$1/').getCollectionValue(node);
print('Title: $title');
print('Author: $author');
print('Date: $date');
print('Paragraphs: ${content.length}');
print('Images: $images');
}
Working with JSON APIs #
import 'dart:convert';
import 'package:web_query/query.dart';
import 'package:http/http.dart' as http;
Future<void> fetchUserData(String userId) async {
final response = await http.get(
Uri.parse('https://api.example.com/users/$userId')
);
final pageData = PageData(
response.request!.url.toString(),
'<html></html>',
jsonData: response.body
);
final node = pageData.getRootElement();
// Extract user data
final name = QueryString('json:data/user/name').getValue(node);
final email = QueryString('json:data/user/email').getValue(node);
final posts = QueryString('json:data/user/posts/*/title').getCollectionValue(node);
print('Name: $name');
print('Email: $email');
print('Posts: $posts');
}
Filtering and Transforming Lists #
// Get all product names that are in stock and on sale
final products = QueryString(
'*div.product/*span.name/@text?filter=!Out\ of\ Stock sale'
).getCollectionValue(node);
// Get all links, make them absolute, filter PDFs
final pdfLinks = QueryString(
'*a/@href?transform=regexp:/^\/(.+)/${rootUrl}$1/&filter=.pdf'
).getCollectionValue(node);
// Extract prices and format them
final prices = QueryString(
'*span.price/@text?transform=regexp:/\$(\d+\.\d{2})/$1/'
).getCollectionValue(node);
URL Queries #
Query and modify URLs using the url: scheme:
Query URL Components
Extract individual parts of the current page URL:
// Given URL: https://example.com:8080/path/to/resource?page=1&sort=desc#section1
final fullUrl = QueryString('url:').getValue(node);
// "https://example.com:8080/path/to/resource?page=1&sort=desc#section1"
final scheme = QueryString('url:scheme').getValue(node);
// "https"
final host = QueryString('url:host').getValue(node);
// "example.com"
final port = QueryString('url:port').getValue(node);
// "8080"
final path = QueryString('url:path').getValue(node);
// "/path/to/resource"
final query = QueryString('url:query').getValue(node);
// "page=1&sort=desc"
final fragment = QueryString('url:fragment').getValue(node);
// "section1"
final userInfo = QueryString('url:userInfo').getValue(node);
// "" (if present in URL)
final origin = QueryString('url:origin').getValue(node);
// "https://example.com:8080"
Query URL Parameters
Access query parameters as a map or individual values:
// Get all query parameters as a map
final params = QueryString('url:queryParameters').execute(node);
// {"page": "1", "sort": "desc"}
// Get specific parameter
final page = QueryString('url:queryParameters/page').getValue(node);
// "1"
final sort = QueryString('url:queryParameters/sort').getValue(node);
// "desc"
// Non-existent parameter returns null
final missing = QueryString('url:queryParameters/missing').getValue(node);
// null
Modify URLs
Modify URL components using query parameters:
Update Query Parameters
// Add new query parameter
final newUrl = QueryString('url:?newParam=value').getValue(node);
// "https://example.com:8080/path/to/resource?page=1&sort=desc&newParam=value#section1"
// Update existing parameter
final updated = QueryString('url:?page=2').getValue(node);
// "https://example.com:8080/path/to/resource?page=2&sort=desc#section1"
// Multiple parameter updates
final multi = QueryString('url:?page=2&status=active').getValue(node);
// "https://example.com:8080/path/to/resource?page=2&sort=desc&status=active#section1"
// Remove parameters
final removed = QueryString('url:?_remove=sort').getValue(node);
// "https://example.com:8080/path/to/resource?page=1#section1"
// Remove multiple parameters
final removedMulti = QueryString('url:?_remove=page,sort').getValue(node);
// "https://example.com:8080/path/to/resource#section1"
Replace URL Components
Use special _ prefixed parameters to replace URL components:
// Change scheme
final httpUrl = QueryString('url:?_scheme=http').getValue(node);
// "http://example.com:8080/path/to/resource?page=1&sort=desc#section1"
// Change host
final newHost = QueryString('url:?_host=new.com').getValue(node);
// "https://new.com:8080/path/to/resource?page=1&sort=desc#section1"
// Change port
final newPort = QueryString('url:?_port=9000').getValue(node);
// "https://example.com:9000/path/to/resource?page=1&sort=desc#section1"
// Change path
final newPath = QueryString('url:?_path=/new/path').getValue(node);
// "https://example.com:8080/new/path?page=1&sort=desc#section1"
// Change fragment
final newFragment = QueryString('url:?_fragment=section2').getValue(node);
// "https://example.com:8080/path/to/resource?page=1&sort=desc#section2"
// Change userInfo
final withAuth = QueryString('url:?_userInfo=user:pass').getValue(node);
// "https://user:pass@example.com:8080/path/to/resource?page=1&sort=desc#section1"
// Combine multiple changes
final combined = QueryString('url:?_scheme=http&_host=api.example.com&page=2').getValue(node);
// "http://api.example.com:8080/path/to/resource?page=2&sort=desc#section1"
URL with Transforms
Apply transforms to URLs or extracted components:
// Extract domain using regexp
final domain = QueryString('url:?regexp=/https:\\/\\/([^\\/]+).*/$1/').getValue(node);
// "example.com:8080"
// Extract just hostname
final hostname = QueryString('url:host?regexp=/([^:]+).*/$1/').getValue(node);
// "example.com"
// Transform scheme
final transformed = QueryString('url:scheme?transform=upper').getValue(node);
// "HTTPS"
// Modify and extract
final modifiedHost = QueryString('url:host?_host=changed.com').getValue(node);
// "changed.com"
// Complex transformation
final apiUrl = QueryString('url:?_scheme=https&_host=api.example.com&_path=/v1/users®exp=/(.*)\\?.*/$1/').getValue(node);
// "https://api.example.com/v1/users"
Practical URL Examples
// Build API endpoint from current URL
final apiEndpoint = QueryString('url:origin?regexp=/(.*)/$1\\/api\\/v1/').getValue(node);
// "https://example.com:8080/api/v1"
// Get base URL without query params
final baseUrl = QueryString('url:?regexp=/([^?]+).*/\$1/').getValue(node);
// "https://example.com:8080/path/to/resource"
// Change to different environment
final stagingUrl = QueryString('url:?_host=staging.example.com&_scheme=https').getValue(node);
DataQueryWidget #
Visual component for interactive data querying:
import 'package:web_query/ui.dart';
DataQueryWidget(
pageData: pageData,
title: 'Query Data',
onToggleExpand: () => setState(() => expanded = !expanded),
)
Features:
- Interactive HTML tree view
- Real-time QueryString filtering
- Switch between HTML and JSON views
- Visual query results panel
See the example project for a complete demonstration.
## API Reference
### QueryString Class
Main class for creating and executing queries.
```dart
// Constructor
QueryString(String query)
// Methods
dynamic execute(PageNode node, {bool simplify = true})
String getValue(PageNode node, {String separator = '\n'})
Iterable<PageNode> getCollection(PageNode node)
Iterable getCollectionValue(PageNode node)
Which Method to Use?
Choose the appropriate method based on your needs:
| Method | Use When | Returns |
|---|---|---|
getValue() |
You want a single string value or concatenated text | String |
getCollectionValue() |
You want multiple raw values (strings, Elements, JSON) | Iterable<dynamic> |
getCollection() |
You need PageNode objects for further querying | Iterable<PageNode> |
execute() |
You want automatic simplification (single value or list) | dynamic |
Recommended: Use getValue() for single values and getCollectionValue() for lists of raw values and getCollection() for lists of PageNode objects. Use execute() only when you need dynamic behavior.
// β
Preferred: Explicit methods
final title = QueryString('h1/@text').getValue(node);
final nodes = QueryString('*li').getCollection(node);
final items = QueryString('*li/@text').getCollectionValue(node);
// β οΈ Less clear: Generic execute
final title = QueryString('h1/@text').execute(node);
final items = QueryString('*li/@text').execute(node);
execute()
Executes the query and returns results.
final result = QueryString('div/@text').execute(node);
// Returns: String (single result) or List (multiple results)
final result = QueryString('div/@text').execute(node, simplify: false);
// Returns: List<PageNode> (always a list)
getValue()
Returns results as a concatenated string.
final text = QueryString('*p/@text').getValue(node);
// Returns: "Para 1\nPara 2\nPara 3"
final text = QueryString('*p/@text').getValue(node, separator: ' | ');
// Returns: "Para 1 | Para 2 | Para 3"
getCollection()
Returns results as PageNode objects.
final nodes = QueryString('*div').getCollection(node);
// Returns: Iterable<PageNode>
for (var node in nodes) {
print(node.element?.text);
}
getCollectionValue()
Returns raw values (Elements or JSON data).
final elements = QueryString('*div').getCollectionValue(node);
// Returns: Iterable<Element>
final jsonData = QueryString('json:items/*').getCollectionValue(node);
// Returns: Iterable<dynamic> (JSON objects)
PageData Class #
Represents a web page with HTML and optional JSON data.
// Constructor
PageData(String url, String html, {String? jsonData})
// Methods
PageNode getRootElement()
PageNode Class #
Represents a node in the query result (HTML element or JSON data).
// Properties
Element? element // HTML element (if HTML query)
dynamic jsonData // JSON data (if JSON query)
PageData pageData // Reference to page data
UI Components #
The package includes interactive UI components for data visualization:
DataQueryWidget #
Complete data reader with HTML/JSON views and query filtering:
import 'package:web_query/ui.dart';
DataQueryWidget(
pageData: pageData,
title: 'Query Data',
onToggleExpand: () => setState(() => expanded = !expanded),
)
JsonTreeView #
Collapsible JSON tree viewer:
import 'package:web_query/ui.dart';
JsonTreeView(json: jsonData)
HtmlTreeView #
Collapsible HTML tree viewer:
import 'package:web_query/ui.dart';
HtmlTreeView(document: parsedDocument)
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.