Skip to content

RSDK-10304: Button component #363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions example/viam_robot_example_app/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:viam_sdk/viam_sdk.dart';
import 'package:viam_sdk/widgets.dart';

import 'screens/button.dart';
import 'screens/screens.dart';

void main() async {
Expand Down Expand Up @@ -88,6 +89,7 @@ class _MyHomePageState extends State<MyHomePage> {
return [
Base.subtype.resourceSubtype,
Board.subtype.resourceSubtype,
Button.subtype.resourceSubtype,
Camera.subtype.resourceSubtype,
Gripper.subtype.resourceSubtype,
Motor.subtype.resourceSubtype,
Expand All @@ -113,6 +115,9 @@ class _MyHomePageState extends State<MyHomePage> {
if (rname.subtype == Board.subtype.resourceSubtype) {
return BoardScreen(board: Board.fromRobot(_robot, rname.name), resourceName: rname);
}
if (rname.subtype == Button.subtype.resourceSubtype) {
return ButtonScreen(button: Button.fromRobot(_robot, rname.name), resourceName: rname);
}
if (rname.subtype == Camera.subtype.resourceSubtype) {
return StreamScreen(camera: Camera.fromRobot(_robot, rname.name), client: _getStream(rname), resourceName: rname);
}
Expand Down
34 changes: 34 additions & 0 deletions example/viam_robot_example_app/lib/screens/button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

import 'package:viam_sdk/viam_sdk.dart';
import 'package:viam_sdk/widgets.dart';

class ButtonScreen extends StatelessWidget {
final Button button;
final ResourceName resourceName;

const ButtonScreen({super.key, required this.button, required this.resourceName});

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(resourceName.name.toUpperCase()),
),
body: Center(
child: Column(
children: [
const SizedBox(height: 8),
Text(
'${resourceName.namespace}:${resourceName.type}:${resourceName.subtype}/${resourceName.name}',
style: const TextStyle(fontWeight: FontWeight.w300),
),
const SizedBox(height: 8),
ViamButtonWidget(button: button),
],
),
),
);
}
}
43 changes: 43 additions & 0 deletions lib/src/components/button/button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import '../../gen/common/v1/common.pb.dart';
import '../../resource/base.dart';
import '../../robot/client.dart';

/// {@category Components}
/// Button represents a button component.
///
/// For more information, see [Button component](https://docs.viam.com/dev/reference/apis/components/button/).
abstract class Button extends Resource {
static const Subtype subtype = Subtype(resourceNamespaceRDK, resourceTypeComponent, 'button');

/// Push the button.
///
/// ```
/// await myButton.push();
/// ```
///
/// For more information, see [Button component](https://docs.viam.com/dev/reference/apis/components/button/#push).
Future<void> push({Map<String, dynamic>? extra});

/// Get the [ResourceName] for this [Button] with the given [name].
///
/// ```
/// // Example:
/// var name = Button.getResourceName('my_button');
/// ```
///
/// For more information, see [Button component](https://docs.viam.com/dev/reference/apis/components/button/#getresourcename).
static ResourceName getResourceName(String name) {
return Button.subtype.getResourceName(name);
}

/// Get the [Button] named [name] from the provided robot.
///
/// ```
/// final myButton = Button.fromRobot(myRobotClient, "my_button");
/// ```
///
/// For more information, see [Button component](https://docs.viam.com/dev/reference/apis/components/button/).
static Button fromRobot(RobotClient robot, String name) {
return robot.getResource(Button.getResourceName(name));
}
}
40 changes: 40 additions & 0 deletions lib/src/components/button/client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import 'package:grpc/grpc_connection_interface.dart';

import '../../gen/common/v1/common.pb.dart';
import '../../gen/component/button/v1/button.pbgrpc.dart';
import '../../gen/google/protobuf/struct.pb.dart';
import '../../resource/base.dart';
import '../../utils.dart';
import 'button.dart';

/// {@category Components}
/// gRPC client for the [Button] component.
class ButtonClient extends Button implements ResourceRPCClient {
@override
final String name;

@override
ClientChannelBase channel;

@override
ButtonServiceClient get client => ButtonServiceClient(channel);

ButtonClient(this.name, this.channel);

@override
Future<void> push({Map<String, dynamic>? extra}) async {
final request = PushRequest()
..name = name
..extra = extra?.toStruct() ?? Struct();
await client.push(request);
}

@override
Future<Map<String, dynamic>> doCommand(Map<String, dynamic> command) async {
final request = DoCommandRequest()
..name = name
..command = command.toStruct();
final response = await client.doCommand(request);
return response.result.toMap();
}
}
37 changes: 37 additions & 0 deletions lib/src/components/button/service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:grpc/grpc.dart';

import '../../gen/common/v1/common.pb.dart';
import '../../gen/component/button/v1/button.pbgrpc.dart';
import '../../resource/manager.dart';
import '../../utils.dart';
import 'button.dart';

/// {@category Components}
/// gRPC Service for a [Button]
class ButtonService extends ButtonServiceBase {
final ResourceManager _manager;

ButtonService(this._manager);

Button _fromManager(String name) {
try {
return _manager.getResource(Button.getResourceName(name));
} catch (e) {
throw GrpcError.notFound(e.toString());
}
}

@override
Future<PushResponse> push(ServiceCall call, PushRequest request) async {
final button = _fromManager(request.name);
await button.push(extra: request.extra.toMap());
return PushResponse();
}

@override
Future<DoCommandResponse> doCommand(ServiceCall call, DoCommandRequest request) async {
final button = _fromManager(request.name);
final result = await button.doCommand(request.command.toMap());
return DoCommandResponse()..result = result.toStruct();
}
}
3 changes: 3 additions & 0 deletions lib/src/resource/registry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import '../components/base/base.dart';
import '../components/base/client.dart';
import '../components/board/board.dart';
import '../components/board/client.dart';
import '../components/button/button.dart';
import '../components/button/client.dart';
import '../components/camera/camera.dart';
import '../components/camera/client.dart';
import '../components/gantry/client.dart';
Expand Down Expand Up @@ -62,6 +64,7 @@ class Registry {
registerSubtype(ResourceRegistration(Arm.subtype, (name, channel) => ArmClient(name, channel)));
registerSubtype(ResourceRegistration(Board.subtype, (name, channel) => BoardClient(name, channel)));
registerSubtype(ResourceRegistration(Base.subtype, (name, channel) => BaseClient(name, channel)));
registerSubtype(ResourceRegistration(Button.subtype, (name, channel) => ButtonClient(name, channel)));
registerSubtype(ResourceRegistration(Camera.subtype, (name, channel) => CameraClient(name, channel)));
registerSubtype(ResourceRegistration(Gantry.subtype, (name, channel) => GantryClient(name, channel)));
registerSubtype(ResourceRegistration(Generic.subtype, (name, channel) => GenericClient(name, channel)));
Expand Down
2 changes: 2 additions & 0 deletions lib/viam_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export 'src/components/base/base.dart';
export 'src/components/base/client.dart';
export 'src/components/board/board.dart';
export 'src/components/board/client.dart';
export 'src/components/button/button.dart';
export 'src/components/button/client.dart';
export 'src/components/camera/camera.dart';
export 'src/components/camera/client.dart';
export 'src/components/gantry/client.dart';
Expand Down
1 change: 1 addition & 0 deletions lib/widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export 'widgets/multi_camera_stream.dart';
export 'widgets/refreshable_data_table.dart';
export 'widgets/resources/base.dart';
export 'widgets/resources/board.dart';
export 'widgets/resources/button.dart';
export 'widgets/resources/gripper.dart';
export 'widgets/resources/motor.dart';
export 'widgets/resources/sensor.dart';
Expand Down
57 changes: 57 additions & 0 deletions lib/widgets/resources/button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'package:flutter/material.dart';

import '../../viam_sdk.dart';
import '../button.dart';

class ViamButtonWidget extends StatefulWidget {
final Button button;

const ViamButtonWidget({super.key, required this.button});

@override
State<ViamButtonWidget> createState() {
return _ViamButtonWidgetState();
}
}

class _ViamButtonWidgetState extends State<ViamButtonWidget> {
bool isLoading = false;
Error? error;

Future<void> _pushButton() async {
try {
setState(() {
isLoading = true;
error = null;
});
await widget.button.push();
} catch (e) {
setState(() {
error = e as Error;
});
} finally {
if (mounted) {
setState(() {
isLoading = false;
});
}
}
}

@override
Widget build(BuildContext context) {
return Column(
children: [
ViamButton(
text: 'Push',
onPressed: isLoading ? null : _pushButton,
),
if (error != null)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Text('Error: $error', style: const TextStyle(color: Colors.red)),
),
],
);
}
}
8 changes: 6 additions & 2 deletions lib/widgets/resources/switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class _ViamSwitchWidgetState extends State<ViamSwitchWidget> {
});
}
} catch (e) {
error = e as Error;
setState(() {
error = e as Error;
});
}
}

Expand All @@ -45,7 +47,9 @@ class _ViamSwitchWidgetState extends State<ViamSwitchWidget> {
await widget.nswitch.setPosition(position);
await _getPosition();
} catch (e) {
error = e as Error;
setState(() {
error = e as Error;
});
}
}

Expand Down
Loading