Skip to main content

Platform Services

Drift provides access to native platform capabilities through the platform package.

Clipboard

Copy and paste text:

import "github.com/go-drift/drift/pkg/platform"

// Copy text to clipboard
err := platform.Clipboard.SetText("Hello, World!")

// Get text from clipboard
text, err := platform.Clipboard.GetText()

// Check if clipboard has text
hasText, err := platform.Clipboard.HasText()

// Clear clipboard
err := platform.Clipboard.Clear()

Example: Copy Button

widgets.NewButton("Copy Link", func() {
platform.Clipboard.SetText(shareURL)
platform.Haptics.LightImpact()
showToast("Link copied!")
})

Haptic Feedback

Provide tactile feedback to users:

// Light feedback for selections
platform.Haptics.LightImpact()

// Medium feedback for toggles and confirmations
platform.Haptics.MediumImpact()

// Heavy feedback for errors or warnings
platform.Haptics.HeavyImpact()

// Selection change feedback
platform.Haptics.SelectionClick()

// Custom vibration duration (milliseconds)
platform.Haptics.Vibrate(100)

When to Use Haptics

Feedback TypeUse Case
LightImpactList item selection, minor interactions
MediumImpactToggle switches, button taps
HeavyImpactErrors, deletions, significant actions
SelectionClickPicker value changes, slider movements

App Lifecycle

Respond to app lifecycle state changes:

func (s *myState) InitState() {
removeHandler := platform.Lifecycle.AddHandler(func(state platform.LifecycleState) {
switch state {
case platform.LifecycleStateResumed:
// App came to foreground
s.refreshData()
case platform.LifecycleStatePaused:
// App went to background
s.saveState()
case platform.LifecycleStateInactive:
// App is transitioning (e.g., receiving a phone call)
case platform.LifecycleStateDetached:
// App is detached from any view
}
})

// Clean up when widget is disposed
s.OnDispose(removeHandler)
}

Lifecycle States

StateDescription
LifecycleStateResumedApp is visible and responding to user input
LifecycleStateInactiveApp is transitioning (system dialog, app switcher)
LifecycleStatePausedApp is not visible but still running
LifecycleStateDetachedApp is detached from any view

Checking Current State

// Get current state
currentState := platform.Lifecycle.State()

// Convenience methods
if platform.Lifecycle.IsResumed() {
// App is active
}

if platform.Lifecycle.IsPaused() {
// App is in background
}

System UI

Customize the status bar and system chrome:

// Set system UI style
platform.SetSystemUI(platform.SystemUIStyle{
StatusBarHidden: false,
StatusBarStyle: platform.StatusBarStyleLight, // or StatusBarStyleDark
TitleBarHidden: false, // Android only
Transparent: false, // Android only
BackgroundColor: &colors.Surface, // Android only
})

Permissions

Request runtime permissions:

// Request a single permission
result, err := platform.RequestPermission(platform.PermissionCamera)
if result == platform.PermissionGranted {
openCamera()
}

// Check current permission status
status, err := platform.CheckPermission(platform.PermissionLocation)

// Request multiple permissions
results, err := platform.RequestPermissions([]platform.Permission{
platform.PermissionCamera,
platform.PermissionMicrophone,
})

// Open app settings for manual permission management
platform.OpenAppSettings()

Available Permissions

PermissionUse
PermissionCameraCamera access
PermissionMicrophoneMicrophone access
PermissionLocationLocation services
PermissionLocationAlwaysBackground location
PermissionStorageFile storage
PermissionContactsContacts access
PermissionPhotosPhoto library
PermissionCalendarCalendar access
PermissionNotificationsPush notifications

Permission Results

ResultMeaning
PermissionGrantedPermission was granted
PermissionDeniedPermission was denied
PermissionPermanentlyDeniedUser selected "Don't ask again"
PermissionRestrictedRestricted by device policy
PermissionLimitedLimited access granted (iOS photos)

Notifications

Schedule local notifications:

// Schedule a notification
err := platform.ScheduleLocalNotification(platform.NotificationRequest{
ID: "reminder-1",
Title: "Reminder",
Body: "Meeting in 5 minutes",
At: time.Now().Add(5 * time.Minute),
Data: map[string]any{"meetingId": "123"},
})

// Cancel a notification
platform.CancelLocalNotification("reminder-1")

// Cancel all notifications
platform.CancelAllLocalNotifications()

// Set app badge count
platform.SetNotificationBadge(3)

Listening for Notifications

// Listen for delivered notifications
go func() {
for event := range platform.Notifications() {
drift.Dispatch(func() {
handleNotification(event)
})
}
}()

// Listen for notification opens (user tapped notification)
go func() {
for open := range platform.NotificationOpens() {
drift.Dispatch(func() {
navigateToContent(open.Data)
})
}
}()

Share

Open the native share sheet:

// Share text
result, err := platform.Share.ShareText("Check out this app!")

// Share text with subject (for email)
result, err := platform.Share.ShareTextWithSubject("Check out this!", "App Recommendation")

// Share a URL
result, err := platform.Share.ShareURL("https://example.com")

// Share URL with text
result, err := platform.Share.ShareURLWithText("https://example.com", "Check out this link!")

// Share a file
result, err := platform.Share.ShareFile("/path/to/file.pdf", "application/pdf")

Location

Access device location:

// Get current location
location, err := platform.GetCurrentLocation(platform.LocationOptions{
HighAccuracy: true,
})
if err == nil {
fmt.Printf("Lat: %f, Lng: %f\n", location.Latitude, location.Longitude)
}

// Start continuous location updates
platform.StartLocationUpdates(platform.LocationOptions{
HighAccuracy: true,
DistanceFilter: 10, // meters
})

// Listen for updates
go func() {
for update := range platform.LocationUpdates() {
drift.Dispatch(func() {
s.SetState(func() {
s.userLocation = update
})
})
}
}()

// Stop updates when done
platform.StopLocationUpdates()

Location Data

FieldTypeDescription
Latitudefloat64Latitude in degrees
Longitudefloat64Longitude in degrees
Altitudefloat64Altitude in meters
Accuracyfloat64Accuracy in meters
Headingfloat64Direction in degrees
Speedfloat64Speed in m/s
Timestamptime.TimeWhen reading was taken

File Storage

Access files and directories:

// Read a file
data, err := platform.ReadFile("/path/to/file.txt")

// Write a file
err := platform.WriteFile("/path/to/file.txt", []byte("content"))

// Delete a file
err := platform.DeleteFile("/path/to/file.txt")

// Get file info
info, err := platform.GetFileInfo("/path/to/file.txt")

// Get app directory
docsPath, err := platform.GetAppDirectory(platform.AppDirectoryDocuments)
cachePath, err := platform.GetAppDirectory(platform.AppDirectoryCache)

File Picker

// Open file picker
platform.PickFile(platform.PickFileOptions{
AllowMultiple: false,
AllowedTypes: []string{"image/*", "application/pdf"},
})

// Listen for results
go func() {
for result := range platform.StorageResults() {
if result.Cancelled {
continue
}
for _, file := range result.Files {
handleSelectedFile(file)
}
}
}()

Thread Safety

Platform services are safe to call from any goroutine. However, when updating UI state from platform callbacks, use drift.Dispatch:

go func() {
for update := range platform.LocationUpdates() {
// Called from background goroutine
drift.Dispatch(func() {
// Now safe to update UI
s.SetState(func() {
s.location = update
})
})
}
}()

Next Steps