Dart 3.11 is live!Learn more
Patterns
Summary of patterns in Dart.
Patterns require alanguage version of at least 3.0.
Patterns are a syntactic category in the Dart language, like statements and expressions. A pattern represents the shape of a set of values that it may match against actual values.
This page describes:
- What patterns do.
- Where patterns are allowed in Dart code.
- What the common use cases for patterns are.
To learn about the different kinds of patterns, visit thepattern types page.
What patterns do
#In general, a pattern maymatch a value,destructure a value, or both, depending on the context and shape of the pattern.
First,pattern matching allows you to check whether a given value:
- Has a certain shape.
- Is a certain constant.
- Is equal to something else.
- Has a certain type.
Then,pattern destructuring provides you with a convenient declarative syntax to break that value into its constituent parts. The same pattern can also let you bind variables to some or all of those parts in the process.
Matching
#A pattern always tests against a value to determine if the value has the form you expect. In other words, you are checking if the valuematches the pattern.
What constitutes a match depends onwhat kind of pattern you are using. For example, a constant pattern matches if the value is equal to the pattern's constant:
switch(number){// Constant pattern matches if 1 == number.case1:print('one');}Many patterns make use of subpatterns, sometimes calledouter andinner patterns, respectively. Patterns match recursively on their subpatterns. For example, the individual fields of anycollection-type pattern could bevariable patterns orconstant patterns:
consta='a';constb='b';switch(obj){// List pattern [a, b] matches obj first if obj is a list with two fields,// then if its fields match the constant subpatterns 'a' and 'b'.case[a,b]:print('$a,$b');}To ignore parts of a matched value, you can use awildcard pattern as a placeholder. In the case of list patterns, you can use arest element.
Destructuring
#When an object and pattern match, the pattern can then access the object's data and extract it in parts. In other words, the patterndestructures the object:
varnumList=[1,2,3];// List pattern [a, b, c] destructures the three elements from numList...var[a,b,c]=numList;// ...and assigns them to new variables.print(a+b+c); You can nestany kind of pattern inside a destructuring pattern. For example, this case pattern matches and destructures a two-element list whose first element is'a' or'b':
switch(list){case['a'||'b',varc]:print(c);}Places patterns can appear
#You can use patterns in several places in the Dart language:
- Local variabledeclarations andassignments
- for and for-in loops
- if-case andswitch-case
- Control flow incollection literals
This section describes common use cases for matching and destructuring with patterns.
Variable declaration
#You can use apattern variable declaration anywhere Dart allows local variable declaration. The pattern matches against the value on the right of the declaration. Once matched, it destructures the value and binds it to new local variables:
// Declares new variables a, b, and c.var(a,[b,c])=('str',[1,2]); A pattern variable declaration must start with eithervar orfinal, followed by a pattern.
Variable assignment
#Avariable assignment pattern falls on the left side of an assignment. First, it destructures the matched object. Then it assigns the values toexisting variables, instead of binding new ones.
Use a variable assignment pattern to swap the values of two variables without declaring a third temporary one:
var(a,b)=('left','right');(b,a)=(a,b);// Swap.print('$a$b');// Prints "right left".Switch statements and expressions
#Every case clause contains a pattern. This applies toswitch statements andexpressions, as well asif-case statements. You can useany kind of pattern in a case.
Case patterns arerefutableRefutable patternA pattern that can be tested against a value.Learn more. They allow control flow to either:
- Match and destructure the object being switched on.
- Continue execution if the object doesn't match.
The values that a pattern destructures in a case become local variables. Their scope is only within the body of that case.
switch(obj){// Matches if 1 == obj.case1:print('one');// Matches if the value of obj is between the// constant values of 'first' and 'last'.case>=first&&<=last:print('in range');// Matches if obj is a record with two fields,// then assigns the fields to 'a' and 'b'.case(vara,varb):print('a =$a, b =$b');default:}Logical-or patterns are useful for having multiple cases share a body in switch expressions or statements:
varisPrimary=switch(color){Color.red||Color.yellow||Color.blue=>true,_=>false,};Switch statements can have multiple cases share a bodywithout using logical-or patterns, but they are still uniquely useful for allowing multiple cases to share aguard:
switch(shape){caseSquare(size:vars)||Circle(size:vars)whens>0:print('Non-empty symmetric shape');}Guard clauses evaluate an arbitrary condition as part of a case, without exiting the switch if the condition is false (like using anif statement in the case body would cause).
switch(pair){case(inta,intb):if(a>b)print('First element greater');// If false, prints nothing and exits the switch.case(inta,intb)whena>b:// If false, prints nothing but proceeds to next case.print('First element greater');case(inta,intb):print('First element not greater');}For and for-in loops
#You can use patterns infor and for-in loops to iterate-over and destructure values in a collection.
This example usesobject destructuring in a for-in loop to destructure theMapEntry objects that a<Map>.entries call returns:
Map<String,int>hist={'a':23,'b':100};for(varMapEntry(key:key,value:count)inhist.entries){print('$key occurred$count times');} The object pattern checks thathist.entries has the named typeMapEntry, and then recurses into the named field subpatternskey andvalue. It calls thekey getter andvalue getter on theMapEntry in each iteration, and binds the results to local variableskey andcount, respectively.
Binding the result of a getter call to a variable of the same name is a common use case, so object patterns can also infer the getter name from thevariable subpattern. This allows you to simplify the variable pattern from something redundant likekey: key to just:key:
for(varMapEntry(:key,value:count)inhist.entries){print('$key occurred$count times');}Use cases for patterns
#Theprevious section describeshow patterns fit into other Dart code constructs. You saw some interesting use cases as examples, likeswapping the values of two variables, ordestructuring key-value pairs in a map. This section describes even more use cases, answering:
- When and why you might want to use patterns.
- What kinds of problems they solve.
- Which idioms they best suit.
Destructuring multiple returns
#Records allow aggregating andreturning multiple values from a single function call. Patterns add the ability to destructure a record's fields directly into local variables, inline with the function call.
Instead of individually declaring new local variables for each record field, like this:
varinfo=userInfo(json);varname=info.$1;varage=info.$2;You can destructure the fields of a record that a function returns into local variables using avariable declaration orassigment pattern, and arecord pattern as its subpattern:
var(name,age)=userInfo(json);To destructure a record with named fields using a pattern:
final(:name,:age)=getData();// For example, return (name: 'doug', age: 25);Destructuring class instances
#Object patterns match against named object types, allowing you to destructure their data using the getters the object's class already exposes.
To destructure an instance of a class, use the named type, followed by the properties to destructure enclosed in parentheses:
finalFoomyFoo=Foo(one:'one',two:2);varFoo(:one,:two)=myFoo;print('one$one, two$two');Algebraic data types
#Object destructuring and switch cases are conducive to writing code in analgebraic data type style. Use this method when:
- You have a family of related types.
- You have an operation that needs specific behavior for each type.
- You want to group that behavior in one place instead of spreading it across all the different type definitions.
Instead of implementing the operation as an instance method for every type, keep the operation's variations in a single function that switches over the subtypes:
sealedclassShape{}classSquareimplementsShape{finaldoublelength;Square(this.length);}classCircleimplementsShape{finaldoubleradius;Circle(this.radius);}doublecalculateArea(Shapeshape)=>switch(shape){Square(length:varl)=>l*l,Circle(radius:varr)=>math.pi*r*r,};Validating incoming JSON
#Map andlist patterns work well for destructuring key-value pairs in deserialized data, such as data parsed from JSON:
vardata={'user':['Lily',13],};var{'user':[name,age]}=data;If you know that the JSON data has the structure you expect, the previous example is realistic. But data typically comes from an external source, like over the network. You need to validate it first to confirm its structure.
Without patterns, validation is verbose:
if(dataisMap<String,Object?>&&data.length==1&&data.containsKey('user')){varuser=data['user'];if(userisList<Object>&&user.length==2&&user[0]isString&&user[1]isint){varname=user[0]asString;varage=user[1]asint;print('User$name is$age years old.');}}A singlecase pattern can achieve the same validation. Single cases work best asif-case statements. Patterns provide a more declarative, and much less verbose method of validating JSON:
if(datacase{'user':[Stringname,intage]}){print('User$name is$age years old.');}This case pattern simultaneously validates that:
jsonis a map, because it must first match the outermap pattern to proceed.- And, since it's a map, it also confirms
jsonis not null.
- And, since it's a map, it also confirms
jsoncontains a keyuser.- The key
userpairs with a list of two values. - The types of the list values are
Stringandint. - The new local variables to hold the values are
nameandage.
Unless stated otherwise, the documentation on this site reflects Dart 3.11.0. Page last updated on 2025-02-24.View source orreport an issue.