Could be useful to have a laoding state for buttons.
We could do it with ActivityIndicator component, but eva design system rules does not allow to add anything other than string in buttons, as related in this issue #488
Hi โ
Thanks for the proposal. This is already under discussion within our team :)
Let's keep it open. I'll share a status
Hi,
This workaround, work for me:
I'm use the prop icon -> https://github.com/akveo/react-native-ui-kitten/blob/master/src/framework/ui/button/button.component.tsx#L274
import {
Button,
} from 'react-native-ui-kitten';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import {
ActivityIndicator,
} from 'react-native';
export default class ScButton extends Component {
static get defaultProps() {
return {
loading: false,
loadingColor: '#FFF',
loadingSize: 'small',
children: null,
};
}
static get propTypes() {
return {
loading: PropTypes.bool,
loadingColor: PropTypes.string,
loadingSize: PropTypes.string,
children: PropTypes.string,
};
}
renderChildren() {
const {
loading,
children,
} = this.props;
if (loading) {
return null;
}
return children;
}
renderLoading() {
const {
loadingColor,
loadingSize,
} = this.props;
return (
<ActivityIndicator
color={loadingColor}
size={loadingSize}
/>
);
}
render() {
const {
loading,
} = this.props;
const customProps = {};
if (loading) {
Object.assign(customProps, {
icon: () => this.renderLoading(),
disabled: true,
});
}
return (
<Button
{...this.props}
{...customProps}
>
{this.renderChildren()}
</Button>
);
}
}
import ScButton from './ScButton';
<ScButton loading>
ButtonText
</ScButton>

@guilhermelcn good job :) But this will break your build if you'll try do this with TypeScript
I also have a workaround:
const [loading, setLoading] = useState(false);
const toggleLoading = useCallback(() => {
setLoading(!loading);
}, [loading]);
<Button icon={loading ? () => <ActivityIndicator /> : null} onPress={toggleLoading}>
Tap me to hide/show loading indicator
</Button>
Hi my approach is a custom button based on the original that have an ActivityIndicator passed from props (similar to react native elements approach). Just have a question @artyorsh how can I put a themed color for make it default color (sorry i'm a bit new in react and ui-kitten ^^)
color={this.props.loadingProps ? this.props.loadingProps.color : 'white'}
type IconProp = (style: ImageStyle) => IconElement;
export interface ButtonProps extends StyledComponentProps, TouchableOpacityProps {
textStyle?: StyleProp<TextStyle>;
loadingStyle?: StyleProp<ViewStyle>;
loadingProps?: ActivityIndicatorProps;
loading?: boolean;
icon?: IconProp;
status?: string;
size?: string;
children?: string;
}
export type ButtonElement = React.ReactElement<ButtonProps>;
export class ButtonComponent extends React.Component<ButtonProps> implements WebEventResponderCallbacks {
static styledComponentName: string = 'Button';
private webEventResponder: WebEventResponderInstance = WebEventResponder.create(this);
// WebEventResponderCallbacks
public onMouseEnter = (): void => {
if (this.props.dispatch) {
this.props.dispatch([Interaction.HOVER]);
}
};
public onMouseLeave = (): void => {
if (this.props.dispatch) {
this.props.dispatch([]);
}
};
public onFocus = (): void => {
if (this.props.dispatch) {
this.props.dispatch([Interaction.FOCUSED]);
}
};
public onBlur = (): void => {
if (this.props.dispatch) {
this.props.dispatch([]);
}
};
private onPress = (event: GestureResponderEvent): void => {
if (this.props.onPress) {
this.props.onPress(event);
}
};
private onPressIn = (event: GestureResponderEvent): void => {
if (this.props.dispatch) {
this.props.dispatch([Interaction.ACTIVE]);
}
if (this.props.onPressIn) {
this.props.onPressIn(event);
}
};
private onPressOut = (event: GestureResponderEvent): void => {
if (this.props.dispatch) {
this.props.dispatch([]);
}
if (this.props.onPressOut) {
this.props.onPressOut(event);
}
};
private getComponentStyle = (source: StyleType): StyleType => {
const {
textColor,
textFontFamily,
textFontSize,
textLineHeight,
textFontWeight,
textMarginHorizontal,
iconWidth,
iconHeight,
iconTintColor,
iconMarginHorizontal,
...containerParameters
} = source;
return {
container: containerParameters,
text: {
color: textColor,
fontFamily: textFontFamily,
fontSize: textFontSize,
lineHeight: textLineHeight,
fontWeight: textFontWeight,
marginHorizontal: textMarginHorizontal,
},
icon: {
width: iconWidth,
height: iconHeight,
tintColor: iconTintColor,
marginHorizontal: iconMarginHorizontal,
},
};
};
private renderTextElement = (style: TextStyle): TextElement => {
return (
<Text
key={1}
style={[style, styles.text, this.props.textStyle]}>
{this.props.children}
</Text>
);
};
private renderActivityIndicatorElement = (style: ViewStyle) => {
return (
<ActivityIndicator
style={StyleSheet.flatten([styles.loading, this.props.loadingStyle, style])}
color={this.props.loadingProps ? this.props.loadingProps.color : 'white'}
size={this.props.loadingProps ? this.props.loadingProps.size : 'small'}
{...this.props.loadingProps}
/>
)
}
private renderIconElement = (style: ImageStyle): IconElement | null => {
if (this.props.icon) {
const iconElement: IconElement = this.props.icon(style);
return React.cloneElement(iconElement, {
key: 2,
style: [style, styles.icon, iconElement.props.style],
});
}
return null;
};
private renderComponentChildren = (style: StyleType): React.ReactNodeArray => {
const { icon, children } = this.props;
return [
icon && this.renderIconElement(style.icon),
isValidString(children as any) && this.renderTextElement(style.text),
this.renderActivityIndicatorElement(style.loading),
];
};
public render(): React.ReactElement<TouchableOpacityProps> {
const { themedStyle, style, ...containerProps } = this.props;
const { container, ...childStyles } = this.getComponentStyle(themedStyle as any);
const [iconElement, textElement, activityElement] = this.renderComponentChildren(childStyles);
return (
<TouchableOpacity
activeOpacity={1.0}
{...containerProps}
{...this.webEventResponder.eventHandlers}
style={[container, styles.container, webStyles.container, style]}
onPress={this.props.loading ? undefined : this.onPress}
onPressIn={this.onPressIn}
onPressOut={this.onPressOut}>
{!this.props.loading && iconElement}
{!this.props.loading && textElement}
{this.props.loading && activityElement}
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
text: {},
icon: {},
loading: {
marginVertical: 2,
},
});
const webStyles = Platform.OS === 'web' && StyleSheet.create({
container: {
// @ts-ignore
outlineWidth: 0,
},
});
export const ButtonCustom = styled<ButtonProps>(ButtonComponent);
@anthowm you can access theme by using withStyles or styled
import { withStyles } from '@ui-kitten/components'
const ScreenComponent = (props) => (
<ActivityIndicator style={props.themedStyle.indicator}/>
);
export const Screen = withStyles(ScreenComponent, theme => ({
indicator: { backgroundColor: theme['color-primary-default'] },
}));
Woop. Soon in v5 ๐(sorry for fps)
@artyorsh Cool I didn't read the v5 roadmap but looks excited ๐ . thanks for the job ๐ฏ
Available in v5! ๐
Most helpful comment
Hi,
This workaround, work for me:
I'm use the prop
icon-> https://github.com/akveo/react-native-ui-kitten/blob/master/src/framework/ui/button/button.component.tsx#L274