Wildcard Variables in Dart
last modified May 25, 2025
Dart 3.8 introduces wildcard variables as a powerful new feature for pattern
matching and destructuring. This tutorial explores how to use the underscore
(_
) as a wildcard to ignore values you don't need in various
contexts.
Wildcard Variables Overview
In Dart, wildcard variables are represented by the underscore character
(_
). They allow you to match and ignore specific values in
patterns, making your code cleaner and more expressive. This feature is
particularly useful in destructuring assignments, pattern matching, and function
parameters where certain values are not needed.
Use Case | Description | Example |
---|---|---|
Destructuring | Ignore specific elements when unpacking | var (x, _, z) = (1, 2, 3); |
Pattern Matching | Match but ignore values in patterns | case [_, int middle, _] |
Function Parameters | Ignore unused parameters | void log(String msg, _) |
Loop Variables | Ignore values in iterations | for (var (_, value) in data) |
The table above summarizes the main use cases for wildcard variables in Dart. Wildcards are versatile and can be applied in various contexts to improve code readability and maintainability. They help you focus on the values that matter while clearly indicating which values are intentionally ignored.
Basic Wildcard Usage
Wildcard variables are most commonly used in destructuring assignments where you want to extract only specific values from a collection or record. The underscore character serves as a visual indicator that certain values are intentionally being ignored.
void main() { // Tuple destructuring with wildcard var (name, _, age) = ('Alice', 'alice@example.com', 30); print('$name is $age years old'); // List destructuring var numbers = [1, 2, 3, 4, 5]; var [first, _, third, ..._] = numbers; print('First: $first, Third: $third'); // Map destructuring (with pattern matching) var user = {'name': 'Bob', 'email': 'bob@example.com', 'age': 25}; var {'name': userName, 'age': userAge} = user; print('$userName is $userAge'); }
In these examples, the wildcard is used to skip email extraction from the tuple and to ignore even-numbered elements in the list. The map destructuring shows an alternative approach where you only extract the fields you need without explicitly ignoring others. The wildcard makes it immediately clear which values are being deliberately omitted from the destructuring operation.
$ dart run basic_wildcard.dart Alice is 30 years old First: 1, Third: 3 Bob is 25
Pattern Matching with Wildcards
Wildcards become particularly powerful when combined with Dart's pattern matching capabilities. They allow you to match complex structures while focusing only on the parts that matter for your logic.
void main() { // Matching lists with wildcards var matrix = [ [1, 2, 3], [4, 5, 6], [7, 8, 9] ]; for (var row in matrix) { switch (row) { case [_, var middle, _]: print('Middle value: $middle'); } } // Matching records with nested wildcards var shapes = [ ('circle', 10.0), ('square', 5.0), ('triangle', (3.0, 4.0, 5.0)) ]; for (var shape in shapes) { switch (shape) { case ('triangle', (_, var b, _)): print('Triangle middle side: $b'); case (var type, _): print('Shape type: $type'); } } }
The first example demonstrates how wildcards can help extract only the middle element from each row of a matrix. The second example shows more advanced usage with nested patterns, where we use wildcards to ignore the shape name when we're only interested in triangle dimensions, and then ignore all dimensions when we just want the shape type. This selective matching makes the code both concise and expressive.
$ dart run pattern_matching.dart Middle value: 2 Middle value: 5 Middle value: 8 Shape type: circle Shape type: square Triangle middle side: 4.0
Function Parameters and Wildcards
Wildcards can clean up function signatures when you need to accept parameters that you won't use. This is common with callback functions that follow a specific interface but don't need all provided arguments.
void main() { // Function with ignored parameter void logError(String message, _) { print('ERROR: $message'); } logError('File not found', 404); // Callback with ignored parameters void processItems(List<String> items, void callback(String, _)) { for (var i = 0; i < items.length; i++) { callback(items[i], i); } } processItems(['Apple', 'Banana', 'Cherry'], (item, _) { print('Processing $item'); }); // Higher-order function with wildcard void Function(String) makeLogger(String prefix) { return (String message) => print('$prefix: $message'); } var debugLog = makeLogger('DEBUG'); debugLog('Initializing'); }
The logError
function shows a simple case where we accept but
ignore a second parameter. The processItems
example demonstrates a
more realistic scenario where a callback receives an index parameter that it
doesn't need. Using wildcards in these cases makes the code's intent clearer
than using named parameters that would go unused. The higher-order function
example shows how wildcards can help maintain clean function signatures even
when dealing with function composition.
$ dart run function_parameters.dart ERROR: File not found Processing Apple Processing Banana Processing Cherry DEBUG: Initializing
Advanced Wildcard Patterns
Wildcards can be combined with other Dart language features to create sophisticated patterns that remain readable and maintainable. These advanced patterns are particularly useful when working with complex data structures.
void main() { // Nested destructuring with wildcards var complexData = [ ('header', {'content': 'Hello', 'meta': 123}), ('footer', {'content': 'Goodbye', 'meta': 456}) ]; for (var (_, {'content': message}) in complexData) { print(message); } // Wildcards in switch expressions var responses = [ (200, 'OK', {'data': 'content'}), (404, 'Not Found', null), (500, 'Error', {'error': 'message'}) ]; for (var response in responses) { var status = switch (response) { (200, _, _) => 'Success', (404, _, _) => 'Missing', (_, _, {'error': _}) => 'Error', _ => 'Unknown' }; print(status); } }
The first example shows nested pattern matching where we extract only the content field from within a map that's itself part of a tuple. The switch expression demonstrates how wildcards can help categorize responses based only on status codes while ignoring other fields. These patterns maintain clarity even when dealing with complex data structures.
$ dart run advanced_patterns.dart Hello Goodbye Success Missing Error
While wildcards are powerful, they should be used judiciously to maintain code clarity. The primary purpose of wildcards is to explicitly ignore values that would otherwise appear to be accidentally unused. Overusing wildcards can make it harder to understand which parts of a data structure are actually being used in the logic.
Source
Dart Language: Patterns
Dart Language Evolution
Wildcard variables in Dart 3.8 provide a clean, expressive way to work with structured data while focusing only on the values that matter. By using the underscore character to explicitly ignore unneeded values, you can write more concise pattern matching code that clearly communicates its intent. This feature is particularly powerful when combined with Dart's other pattern matching capabilities.
Author
List all Dart tutorials.