Skip to content

Latest commit

 

History

History
454 lines (325 loc) · 13.1 KB

create-custom-native-elements.md

File metadata and controls

454 lines (325 loc) · 13.1 KB
title description contributors
Creating Custom Native Elements
What to do when built-in elements don't meet your needs?
NathanWalker

When working with view markup in NativeScript, you interact with a set of pre-registered elements, such as GridLayout, Button, and Label. These are commonly referred to as "view components."

You can also create custom native elements or extend existing ones to add specialized functionality to your application.

This guide demonstrates how to:

  • Register custom view elements across all NativeScript flavors.
  • Implement a simple custom view component, starting from a basic class definition:
import { View } from '@nativescript/core'

export class Checkbox extends View {
  // implementation details
}

Registering New Elements

You could use Checkbox within XML markup by providing any namespace prefix and the path to it.

<Page xmlns="http://schemas.nativescript.org/tns.xsd"
  xmlns:custom="./checkbox">
  <StackLayout>
    <custom:Checkbox width="200" height="200" />
  </StackLayout>
</Page>

The registration can occur anywhere the view is needed or commonly in app.component.ts, the root bootstrap component, making it available for use anywhere in your application.

import { registerElement } from '@nativescript/angular'
import { Checkbox } from './checkbox'

registerElement('Checkbox', () => Checkbox)

You can now use it like anything else:

<StackLayout>
  <Checkbox width="200" height="200" />
</StackLayout>

The registration can be done in the bootsrap file, commonly app.ts. With Svelte, we use camelCase on elements where applicable.

import { registerNativeViewElement } from '@nativescript-community/svelte-native/dom'
import { Checkbox } from './checkbox'

registerNativeViewElement('checkbox', () => Checkbox)

You can now use it like anything else:

<stackLayout>
  <checkbox width="200" height="200" />
</stackLayout>

The registration can be done in the bootsrap file, commonly app.ts. With React, we use camelCase on elements where applicable.

import { registerElement } from 'react-nativescript'
import { Checkbox } from './checkbox'

registerElement('checkbox', () => Checkbox)

You can now use it like anything else:

<stackLayout>
  <checkbox width="200" height="200" />
</stackLayout>

The registration can be done in the bootsrap file, commonly app.tsx.

import { registerElement } from 'dominative'
import { Checkbox } from './checkbox'

registerElement('checkbox', Checkbox)

You can now use it like anything else:

<stacklayout>
  <checkbox width="200" height="200" />
</stacklayout>

The registration can be done in the bootsrap file, commonly app.ts.

import { registerElement } from 'nativescript-vue'
import { Checkbox } from './checkbox'

registerElement('Checkbox', () => Checkbox)

You can now use it like anything else:

<StackLayout>
  <Checkbox width="200" height="200" />
</StackLayout>

Creating Custom View Components

The process for creating new NativeScript view components is consistent across Angular, React, Solid, Svelte, TypeScript, and Vue.

Anatomy of a Custom View

Every custom NativeScript view must have:

  • (required) A class extending any NativeScript View.
  • (required) createNativeView: Construct and return a platform-native view. Note: It's only required if you want to override what's being returned from the extended class.
  • (optional) initNativeView: Perform initialization after creation.
  • (optional) disposeNativeView: Cleanup resources when removed.

Example:

import { View } from '@nativescript/core'

export class CustomView extends View {
  createNativeView() {
    // return UIView or android.view.View instance
  }

  initNativeView() {
    // initialization code
  }

  disposeNativeView() {
    // cleanup code
  }
}

Extending Existing Views

You can extend any existing NativeScript view, for example:

import { ContentView, Label, Button } from '@nativescript/core'

export class CustomView1 extends ContentView {}
export class CustomView2 extends Label {}
export class CustomView3 extends Button {}

createNativeView Examples

This method must return an instance of the native view:

// iOS
createNativeView() {
  return new WKWebView({ frame: CGRectZero, configuration });
}

// Android
createNativeView() {
  return new android.webkit.WebView(this._context);
}

Initialization and Cleanup

  • initNativeView: Perform setup after the view has been created.
  • disposeNativeView: Free resources as needed.

Explore more examples within NativeScript core and plugins:

For additional details on advanced topics like using Cocoapods or Gradle, refer to this blog series.

Project Structure for Custom Views

Create a dedicated folder for your component, such as:

./checkbox
  common.ts
  index.android.ts
  index.ios.ts
  index.d.ts

This structure encapsulates platform-specific implementations and shared logic, simplifying imports and registration:

import { Checkbox } from './checkbox'

Real-World Example: Custom Checkbox

Let's create a <Checkbox> component that behaves consistently on iOS and Android like this:

<iframe style="width: 100%; min-height: 200px; aspect-ratio: 16 / 9;" src="https://www.youtube.com/embed/08uip43irOM" title="Creating custom elements with NativeScript" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

See the full working example on StackBlitz:

In NativeScript, creating custom UI components can be straightforward and powerful. In this guide, you'll learn how to build a simple, reusable Checkbox component using only built-in modules from @nativescript/core. We'll leverage a combination of GridLayout, Label, and Material Design icons.

Step-by-Step Guide

1. Extend GridLayout

The base structure of our Checkbox will utilize GridLayout, which serves as a container for the checkbox icon:

import { GridLayout } from '@nativescript/core'

export class CheckboxCommon extends GridLayout {}

2. Initialize Checkbox with a Font Icon

Next, we'll add a Label to serve as our checkbox icon, using Material Design icons for the visual representation.

import { GridLayout, Label, Color } from '@nativescript/core'

export class CheckboxCommon extends GridLayout {
  checked = false
  defaultColor = new Color('#dddddd')
  selectionColor = new Color('#4CAF50')

  private _iconLabel: Label
  private _circleIcon = String.fromCharCode(0xf764)
  private _checkmarkIcon = String.fromCharCode(0xf5e0)

  constructor() {
    super()

    this.horizontalAlignment = 'center'
    this.verticalAlignment = 'middle'

    this._iconLabel = new Label()
    this._iconLabel.text = this._circleIcon
    this._iconLabel.className = 'mat'
    this._iconLabel.color = this.defaultColor
    this._iconLabel.horizontalAlignment = 'center'
    this._iconLabel.verticalAlignment = 'middle'

    this.addChild(this._iconLabel)
  }
}

Include Material Design Icons in your project by adding this CSS class in your app.css:

.mat {
  font-family: 'Material Design Icons', 'MaterialDesignIcons';
  font-weight: 400;
}

Ensure you've added the MaterialDesignIcons font file to your app's fonts directory.


3. Implementing Animation

To enhance user interaction, add a simple scaling animation when toggling the checkbox state:

import { CoreTypes } from '@nativescript/core'

export class CheckboxCommon extends GridLayout {
  // existing properties and constructor...

  private _animateCheckmark() {
    if (!this.checked) {
      this._iconLabel.color = this.defaultColor
    }

    this._iconLabel
      .animate({
        scale: { x: 0.5, y: 0.5 },
        duration: 200,
        curve: CoreTypes.AnimationCurve.easeInOut,
      })
      .then(() => {
        this._iconLabel.text = this.checked
          ? this._checkmarkIcon
          : this._circleIcon

        if (this.checked) {
          this._iconLabel.color = this.selectionColor
        }

        this._iconLabel.animate({
          scale: { x: 1, y: 1 },
          duration: 200,
          curve: CoreTypes.AnimationCurve.easeInOut,
        })
      })
  }
}

4. Defining Customizable Properties

NativeScript's Property system allows you to easily expose customizable attributes:

const sizeProperty = new Property<CheckboxCommon, number>({
  name: 'size',
  defaultValue: 24,
  affectsLayout: __APPLE__,
})

const checkedProperty = new Property<CheckboxCommon, boolean>({
  name: 'checked',
  defaultValue: false,
  valueConverter: booleanConverter,
})

const defaultColorProperty = new Property<CheckboxCommon, Color>({
  name: 'defaultColor',
  equalityComparer: Color.equals,
  valueConverter: (v) => new Color(v),
})

const selectionColorProperty = new Property<CheckboxCommon, Color>({
  name: 'selectionColor',
  equalityComparer: Color.equals,
  valueConverter: (v) => new Color(v),
})

These properties automatically handle conversions, such as strings to colors or booleans.


5. Connecting Properties to Native Views

Link these properties to your checkbox component:

export class CheckboxCommon extends GridLayout {
  // existing implementation...

  [sizeProperty.setNative](value: number) {
    this._iconLabel.fontSize = value
  }

  [checkedProperty.setNative](value: boolean) {
    this.checked = value
    if (this.isLoaded) {
      this._animateCheckmark()
    }
  }

  [defaultColorProperty.setNative](value: Color) {
    this.defaultColor = value
    if (this._iconLabel) {
      this._iconLabel.color = this.defaultColor
    }
  }

  [selectionColorProperty.setNative](value: Color) {
    this.selectionColor = value
  }
}

// Register properties
sizeProperty.register(CheckboxCommon)
checkedProperty.register(CheckboxCommon)
defaultColorProperty.register(CheckboxCommon)
selectionColorProperty.register(CheckboxCommon)

Complete Example and Testing

Explore and interact with the complete example directly in the flavor you love:


Summary

Creating custom components in NativeScript allows you to:

  • Build complex components from simple elements
  • Utilize powerful styling and animations
  • Easily manage and expose customizable properties

Whether creating fully native views or hybrid custom components, NativeScript provides flexibility and efficiency to meet your application's needs.

Customize Existing Elements?

Rather than create new elements from scratch, you may want to just adjust existing view elements. Learn about how to extend any element for custom behavior here.