<template>
<div>{{ displayString }}</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
displayString: String
});
</script>
Compare Declarative Frameworks
class MyComponent extends StatelessWidget {
final String displayString;
MyComponent({required this.displayString});
Widget build(BuildContext context) {
return Text(displayString);
}
}
@Composable
fun MyComponent(
displayString: String
) {
Text(displayString)
}
<template>
<p v-if="condition">Condition is true</p>
<p v-else>Condition is false</p>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
condition: Boolean
});
</script>
class ConditionalComponent extends StatelessWidget {
final bool condition;
ConditionalComponent({required this.condition});
Widget build(BuildContext context) {
if(condition) {
return Text("Condition is true");
} else {
return Text("Condition is false");
}
}
}
// Usage
ConditionalComponent(condition: true)
@Composable
fun ConditionalComponent(condition: Boolean) {
if (condition) {
Text("Condition is true")
} else {
Text("Condition is false")
}
}
// Usage
ConditionalComponent(condition = true)
<template>
<intermediate-component :data="data" />
</template>
<script setup>
import { defineProps } from 'vue';
import IntermediateComponent from './IntermediateComponent.vue';
const props = defineProps({
data: String
});
</script>
class Parent extends StatelessWidget {
final String data;
Parent({required this.data});
Widget build(BuildContext context) {
return IntermediateComponent(data: data);
}
}
class IntermediateComponent extends StatelessWidget {
final String data;
IntermediateComponent({required this.data});
Widget build(BuildContext context) {
return ChildComponent(data: data);
}
}
class ChildComponent extends StatelessWidget {
final String data;
ChildComponent({required this.data});
Widget build(BuildContext context) {
return Text("Received data: $data");
}
}
// Usage
Parent(data: "Some data")
@Composable
fun Parent(data: String) {
IntermediateComponent(data = data)
}
@Composable
fun IntermediateComponent(data: String) {
ChildComponent(data = data)
}
@Composable
fun ChildComponent(data: String) {
Text("Received data: $data")
}
// Usage
Parent(data = "Some data")
<template>
<button @click="setClicked">
{{ clicked ? "Button clicked" : "Click me" }}
</button>
</template>
<script setup>
import { ref } from 'vue';
const clicked = ref(false);
function setClicked() {
clicked.value = true;
}
</script>
class ClickableComponent extends StatefulWidget {
_ClickableComponentState createState() => _ClickableComponentState();
}
class _ClickableComponentState extends State<ClickableComponent> {
bool clicked = false;
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () => setState(() => clicked = true),
child: Text(clicked ? "Button clicked" : "Click me"),
);
}
}
@Composable
fun ClickableComponent() {
var clicked by remember { mutableStateOf(false) }
Button(onClick = { clicked = true }) {
Text(if (clicked) "Button clicked" else "Click me")
}
}
<template>
<input
type="text"
v-model="text"
placeholder="Enter text"
/>
</template>
<script setup>
import { ref } from 'vue';
const text = ref('');
</script>
class TextInputComponent extends StatefulWidget {
const TextInputComponent({super.key});
State<TextInputComponent> createState() => _TextInputComponentState();
}
class _TextInputComponentState extends State<TextInputComponent> {
late final _controller = TextEditingController(text: "");
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return TextField(
controller: _controller,
decoration: const InputDecoration(labelText: "Enter text"),
);
}
}
@Composable
fun TextInputComponent() {
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { newText -> text = newText },
label = { Text("Enter text") }
)
}
Vue.js doesn't have a built-in preview feature. However, you can use a tool like Storybook to create previews for your components in a separate development environment.
Flutter doesn't have a built-in preview feature. You can, however, create a separate app or run your app in an emulator or on a device to view your components. Additionally, you can use the Flutter Studio web-based tool to create and preview Flutter widgets in a browser.
@Composable
fun ExampleComponent() {
Text("Hello, World!")
}
@Preview(showBackground = true)
@Composable
fun ExampleComponentPreview() {
ExampleComponent()
}
Additionally, you can also use Showkase, an open source library by Airbnb that allows you to view themes preview functions in an auto-generated component browser that can be viewed on an Android device.
<template>
<ul>
<li v-for="item in items" :key="item">
{{ item }}
</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
items: Array
});
</script>
<!-- Usage -->
<list-component :items="['Item 1', 'Item 2', 'Item 3']"></list-component>
class ListComponent extends StatelessWidget {
final List<String> items;
ListComponent({required this.items});
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(title: Text(items[index]));
},
);
}
}
// Usage
final items = ["Item 1", "Item 2", "Item 3"];
ListComponent(items: items)
@Composable
fun ListComponent(items: List<String>) {
LazyColumn {
items(items) { item ->
Text(item)
}
}
}
// Usage
val items = listOf("Item 1", "Item 2", "Item 3")
ListComponent(items = items)
<template>
<ul>
<li v-for="person in items" :key="person.id">
Name: {{ person.name }}, Age: {{ person.age }}
</li>
</ul>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
items: Array
});
</script>
<!-- Usage -->
<item-keys-example
:items="[
{ name: 'John', age: 30, id: '1' },
{ name: 'Jane', age: 28, id: '2' },
{ name: 'Bob', age: 25, id: '3' }
]"
></item-keys-example>
class Person {
final String name;
final int age;
final String id;
Person({required this.name, required this.age, required this.id});
}
class ItemKeysExample extends StatelessWidget {
final List<Person> items;
ItemKeysExample({required this.items});
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final person = items[index];
return ListTile(
key: Key(person.id),
title: Text('Name: ${person.name}, Age: ${person.age}'),
);
},
);
}
}
// Usage
ItemKeysExample(items: [Person(name: 'John', age: 30, id: '1'), Person(name: 'Jane', age: 28, id: '2'), Person(name: 'Bob', age: 25, id: '3')])
data class Person(val name: String, val age: Int, val id: String)
@Composable
fun ItemKeysExample(items: List<Person>) {
LazyColumn {
items(items, key = { person -> person.id }) { person ->
Text("Name: ${person.name}, Age: ${person.age}")
}
}
}
// ParentComponent.vue
<template>
<div>
<slot name="header"></slot>
<slot name="content"></slot>
</div>
</template>
// ChildComponent.vue
<template>
<p>Child Content</p>
</template>
// Usage
<parent-component>
<template v-slot:header>
<h1>Header</h1>
</template>
<template v-slot:content>
<child />
</template>
</parent-component>
class Parent extends StatelessWidget {
final Widget header;
final Widget content;
Parent({required this.header, required this.content});
Widget build(BuildContext context) {
return Column(
children: [
header,
content,
],
);
}
}
// Usage
Parent(
header: Text("Header"),
content: Child(),
)
class Child extends StatelessWidget {
Widget build(BuildContext context) {
return Text("Child Content");
}
}
@Composable
fun Parent(
header: @Composable () -> Unit,
content: @Composable () -> Unit
) {
Column {
header()
content()
}
}
// Usage
Parent(
header = { Text("Header") },
content = { Child() }
)
@Composable
fun Child() {
Text("Child Content")
}
Vue.js doesn't have a direct analog to modifiers in Jetpack Compose or SwiftUI. Instead, you can use inline styles or CSS classes.
<template>
<div :style="style">Hello, World!</div>
</template>
<script setup>
import { reactive } from 'vue';
const style = reactive({
padding: '16px',
backgroundColor: 'blue',
color: 'white'
});
</script>
In Flutter, you can wrap widgets with other widgets to achieve similar effects.
class ModifiersExample extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16.0),
color: Colors.blue,
child: Text('Hello, World!', style: TextStyle(color: Colors.white)),
);
}
}
@Composable
fun ModifiersExample() {
Text(
"Hello, World!",
modifier = Modifier
.padding(16.dp)
.background(Color.Blue)
)
}
<template>
<button @click="incrementCount">
Count: {{ count }}
</button>
</template>
<script setup>
import { ref } from 'vue';
const count = ref(0);
function incrementCount() {
count.value++;
}
</script>
class Counter extends StatefulWidget {
_CounterState createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int count = 0;
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () => setState(() => count += 1),
child: Text("Count: $count"),
);
}
}
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count = count + 1 }) {
Text("Count: $count")
}
}
<!-- ParentComponent.vue -->
<template>
<intermediate />
</template>
<script setup>
import { provide, ref } from 'vue';
import Intermediate from './IntermediateComponent.vue';
const data = ref('Some data');
provide('dataKey', data);
</script>
<!-- IntermediateComponent.vue -->
<template>
<child />
</template>
<script setup>
import Child from './ChildComponent.vue';
</script>
<!-- ChildComponent.vue -->
<template>
<p>Received data: {{ data }}</p>
</template>
<script setup>
import { inject } from 'vue';
const data = inject('dataKey');
</script>
<!-- Usage -->
<parent-component data="Some data"></parent-component>
class CustomInheritedWidget extends InheritedWidget {
final String data;
CustomInheritedWidget({required this.data, required Widget child})
: super(child: child);
bool updateShouldNotify(CustomInheritedWidget oldWidget) {
return oldWidget.data != data;
}
static CustomInheritedWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<CustomInheritedWidget>()!;
}
}
class Parent extends StatelessWidget {
final String data;
Parent({required this.data});
Widget build(BuildContext context) {
return CustomInheritedWidget(
data: data,
child: Intermediate(),
);
}
}
class Intermediate extends StatelessWidget {
Widget build(BuildContext context) {
return Child();
}
}
class Child extends StatelessWidget {
Widget build(BuildContext context) {
final data = CustomInheritedWidget.of(context).data;
return Text("Received data: $data");
}
}
// Usage
Parent(data: "Some data")
val CustomLocal = compositionLocalOf<String> { "Default data" }
@Composable
fun Parent(data: String) {
CompositionLocalProvider(CustomLocal provides data) {
Intermediate()
}
}
@Composable
fun Intermediate() {
Child()
}
@Composable
fun Child() {
val data = CustomLocal.current
Text("Received data: $data")
}
// Usage
Parent(data = "Some data")
<template>
<div></div>
</template>
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
// Perform side effect here
});
</script>
class SideEffectOnLoadComponent extends StatefulWidget {
_SideEffectOnLoadComponentState createState() => _SideEffectOnLoadComponentState();
}
class _SideEffectOnLoadComponentState extends State<SideEffectOnLoadComponent> {
void initState() {
super.initState();
// Perform side effect, e.g. fetch data, update external data source
}
Widget build(BuildContext context) {
// Other UI components
return Container();
}
}
@Composable
fun SideEffectOnLoadComponent() {
LaunchedEffect(Unit) {
// Perform side effect, e.g. fetch data, update external data source
}
// Other UI components
Text("Hello, World!")
}